/*
 * Copyright (c) 2004 Christopher M Butler
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.flexdock.docking;

import java.awt.Component;
import java.awt.Window;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.IOException;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.WeakHashMap;

import javax.swing.SwingUtilities;


import org.flexdock.dockbar.DockbarManager;
import org.flexdock.dockbar.event.DockableEvent;
import org.flexdock.dockbar.event.DockableEventHandler;
import org.flexdock.dockbar.event.DockableListener;
import org.flexdock.docking.activation.ActiveDockableListener;
import org.flexdock.docking.adapter.AdapterFactory;
import org.flexdock.docking.adapter.DockingAdapter;
import org.flexdock.docking.defaults.DefaultDockingStrategy;
import org.flexdock.docking.defaults.DockableComponentWrapper;
import org.flexdock.docking.drag.DragManager;
import org.flexdock.docking.drag.effects.DragPreview;
import org.flexdock.docking.drag.effects.EffectsManager;
import org.flexdock.docking.drag.effects.RubberBand;
import org.flexdock.docking.event.DockingEventHandler;
import org.flexdock.docking.event.hierarchy.DockingPortTracker;
import org.flexdock.docking.event.hierarchy.RootDockingPortInfo;
import org.flexdock.docking.floating.policy.FloatPolicyManager;
import org.flexdock.docking.props.DockablePropertySet;
import org.flexdock.docking.props.PropertyManager;
import org.flexdock.docking.state.DockingState;
import org.flexdock.docking.state.FloatManager;
import org.flexdock.docking.state.LayoutManager;
import org.flexdock.docking.state.MinimizationManager;
import org.flexdock.docking.state.PersistenceException;
import org.flexdock.event.*;
import org.flexdock.util.ClassMapping;
import org.flexdock.util.DockingUtility;
import org.flexdock.util.ResourceManager;
import org.flexdock.util.RootWindow;
import org.flexdock.util.SwingUtility;
import org.flexdock.util.Utilities;

/**
 * This class is used as a public facade into the framework docking system. It
 * provides a straightforward public API for managing and manipulating the
 * various different subcomponents that make up the docking framework through a
 * single class. {@code DockingManager} cannot be instantiated. Rather, its
 * methods are accessed statically from within application code and it generally
 * defers processing to a set of abstract handlers hidden from the application
 * layer.
 * <p>
 * Among {@code DockingManager's} responsibilities are as follows:
 * <p>
 * <dl>
 * <dt>Maintaining a component repository.</dt>
 * <dd> All {@code Dockables} and {@code DockingPorts} are cached within an and
 * accessible through and internal registry. </dd>
 * <dt>Maintaining framework state.</dt>
 * <dd>{@code DockingManager} provides APIs for managing various different
 * global framework settings, including application-key, floating support,
 * auto-persistence, {@code LayoutManagers}, and {@code MinimizationManagers}.
 * </dd>
 * <dt>Behavioral auto-configuration.</dt>
 * <dd> {@code DockingManager} automatically adds and removes necessary event
 * listeners to enable/disable drag-to-dock behavior as components are
 * registered and unregistered. </dd>
 * <dt>Programmatic access to docking operations.</dt>
 * <dd> {@code DockingManager} provides public APIs for programmatically dock,
 * undock, minimize, persist, and load {@code Dockables} from storage. </dd>
 * </dl>
 *
 * @author Christopher Butler
 */
public class DockingManager implements DockingConstants {

	public static final String MINIMIZE_MANAGER = "minimize.manager";

	public static final String LAYOUT_MANAGER = "layout.manager";

	private static final String DEV_PROPS = "org/flexdock/util/dev-props.properties";

	private static final String CONFIG_PROPS = "org/flexdock/docking/flexdock-core.properties";

	private static final DockingManager SINGLETON = new DockingManager();

	private static final HashMap DOCKABLES_BY_ID = new HashMap();

	private static final WeakHashMap DOCKABLES_BY_COMPONENT = new WeakHashMap();

	private static final ClassMapping DOCKING_STRATEGIES = new ClassMapping(
			DefaultDockingStrategy.class, new DefaultDockingStrategy());

	// Map(DockingPort -> MaximizedState)
	private static final Map maximizedStatesByRootPort = new HashMap();

	private static Object persistentIdLock = new Object();

	private static Component applicationWindow; // 应用程序主窗口，主要用于 SetMinimized

	private String defaultLayoutManagerClass;

	private String defaultMinimizeManagerClass;

	private DockingStrategy defaultDocker;

	private LayoutManager layoutManager;

	private MinimizationManager minimizeManager;

	private DockableFactory dockableFactory;

	private AutoPersist autoPersister;

	private float defaultSiblingSize;

	static {
		// call this method to preload any framework resources
		// we might need later
		init();
	}

	private static class AutoPersist extends Thread {
		private boolean enabled;

		public void run() {
			store();
		}

		private synchronized void store() {
			try {
				if (isEnabled())
					storeLayoutModel();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (PersistenceException e) {
				e.printStackTrace();
			}
		}

		public synchronized boolean isEnabled() {
			return enabled;
		}

		public synchronized void setEnabled(boolean enabled) {
			this.enabled = enabled;
		}
	}

	private static class MaximizedState {
		private final Dockable dockable;

		private final DockingPort originalPort;

		public MaximizedState(Dockable dockable, DockingPort originalDockingPort) {
			this.dockable = dockable;
			this.originalPort = originalDockingPort;
		}

		public Dockable getDockable() {
			return dockable;
		}

		public DockingPort getOriginalPort() {
			return originalPort;
		}
	}

	private static void init() {
		// load the dev system properties
		Properties p = ResourceManager.getProperties(DEV_PROPS, true);
		if (p != null)
			System.getProperties().putAll(p);

		// prime the drag manager for use
		DragManager.prime();

		// ensure our Dockable adapters have been loaded
		AdapterFactory.prime();

		// make sure dockingEvents are properly intercepted
		EventManager.addHandler(new DockingEventHandler());
		EventManager.addHandler(new DockableEventHandler());
		EventManager.addListener(FloatPolicyManager.getInstance());

		Properties config = ResourceManager.getProperties(CONFIG_PROPS, true);
		DockingManager mgr = getDockingManager();
		// set the minimization manager
		mgr.defaultMinimizeManagerClass = config.getProperty(MINIMIZE_MANAGER);
		setMinimizeManager(mgr.defaultMinimizeManagerClass);
		// set the layout manager
		mgr.defaultLayoutManagerClass = config.getProperty(LAYOUT_MANAGER);
		setLayoutManager(mgr.defaultLayoutManagerClass);

		// setup tracking for the currently active dockable
		ActiveDockableListener.prime();

		// setup the default sibling size
		float siblingSize = Utilities.getFloat(System
						.getProperty(RegionChecker.DEFAULT_SIBLING_SIZE_KEY),
				RegionChecker.DEFAULT_SIBLING_SIZE);
		setDefaultSiblingSize(siblingSize);

		// setup auto-persistence
		Runtime.getRuntime().addShutdownHook(getDockingManager().autoPersister);
	}

	private DockingManager() {
		defaultDocker = new DefaultDockingStrategy();
		autoPersister = new AutoPersist();
	}

	private static DockingManager getDockingManager() {
		return SINGLETON;
	}

	public static void setApplicationWindow(Component window) {
		applicationWindow = window;
	}

	public static void addDragSource(Dockable dockable, Component dragSrc) {
		List sources = dockable == null ? null : dockable.getDragSources();
		if (sources == null || dragSrc == null)
			return;

		if (!sources.contains(dragSrc)) {
			updateDragListeners(dockable);
		}
	}

	/**
	 * Convenience method that removes the specified {@code Dockable} from the
	 * layout. If the {@code Dockable}is embedded within the main application
	 * frame or a floating dialog, it is removed from the container hierarchy.
	 * If the {@code Dockable} is presently minimized, it is removed from the
	 * current minimization manager. If the {@code Dockable} is already "closed"
	 * or is {@code null}, no operation is performed. "Closing" a
	 * {@code Dockable} only removes it from the visual layout. It does not
	 * remove it from the internal {@code Dockable} registry and all underlying
	 * {@code DockingState} information remains consistent so that the
	 * {@code Dockable} may later be restored to its original location within
	 * the application.
	 *
	 * @param dockable the {@code Dockable} to be closed.
	 */
	public static void close(Dockable dockable) {
		if (dockable == null)
			return;

		if (isMaximized(dockable)) {
			toggleMaximized(dockable);
		}

		if (isDocked(dockable)) {
			EventManager.dispatch(new DockableEvent(dockable, DockableEvent.CLOSING));
			undock(dockable);
			EventManager.dispatch(new DockableEvent(dockable, DockableEvent.CLOSED));
		} else if (DockingUtility.isMinimized(dockable)) {
			EventManager.dispatch(new DockableEvent(dockable, DockableEvent.CLOSING));
			getMinimizeManager().close(dockable);
			EventManager.dispatch(new DockableEvent(dockable, DockableEvent.CLOSED));
		}
	}

	public static void addDockableListener(DockableListener listener) {
		EventManager.addListener(listener);
	}

	public static void removeDockableListener(DockableListener listener) {
		EventManager.removeListener(listener);
	}

	/**
	 * Docks the specified {@code Component} into the CENTER region of the
	 * specified {@code DockingPort}. If the {@code DockingManager} finds a
	 * valid {@code Dockable} instance mapped to the specified {@code Component},
	 * the {@code Dockable} will be docked into the {@code DockingPort}. If the
	 * {@code Component} or {@code DockingPort} is {@code null}, or a valid
	 * {@code Dockable} cannot be found for the specified {@code Component},
	 * this method returns {@code false}. Otherwise, this method returns
	 * {@code true} if the docking operation was successful and {@code false} if
	 * the docking operation cannot be completed. This method defers processing
	 * to {@code dock(Component dockable , DockingPort port, String region)}.
	 *
	 * @param dockable the {@code Component} to be docked.
	 * @param port     the {@code DockingPort} into which the specified
	 *                 {@code Component} will be docked.
	 * @return {@code true} if the docking operation was successful,
	 * {@code false} otherwise.
	 * @see #dock(Component, DockingPort, String)
	 */
	public static boolean dock(Component dockable, DockingPort port) {
		return dock(dockable, port, CENTER_REGION);
	}

	/**
	 * Docks the specified {@code Component} into the supplied region of the
	 * specified {@code DockingPort}. If the {@code DockingManager} finds a
	 * valid {@code Dockable} instance mapped to the specified {@code Component},
	 * the {@code Dockable} will be docked into the {@code DockingPort}. If the
	 * {@code Component} or {@code DockingPort} is {@code null}, or a valid
	 * {@code Dockable} cannot be found for the specified {@code Component},
	 * this method returns {@code false}. Otherwise, this method returns
	 * {@code true} if the docking operation was successful and {@code false} if
	 * the docking operation cannot be completed. This method defers processing
	 * to {@code dock(Dockable dockable, DockingPort port, String region)}.
	 *
	 * @param dockable the {@code Component} to be docked.
	 * @param port     the {@code DockingPort} into which the specified
	 *                 {@code Component} will be docked.
	 * @param region   the region into which to dock the specified {@code Component}
	 * @return {@code true} if the docking operation was successful,
	 * {@code false} if the docking operation cannot be completed.
	 * @see #dock(Dockable, DockingPort, String)
	 */
	public static boolean dock(Component dockable, DockingPort port,
	                           String region) {
		Dockable d = resolveDockable(dockable);
		return dock(d, port, region);
	}

	/**
	 * Docks the specified {@code Dockable} into the supplied region of the
	 * specified {@code DockingPort}. If the {@code Dockable} or
	 * {@code DockingPort} is {@code null}, this method returns {@code false}.
	 * Otherwise, this method returns {@code true} if the docking operation was
	 * successful and {@code false} if the docking operation cannot be
	 * completed.
	 * <p>
	 * This method determines the {@code DockingStrategy} to be used for the
	 * specified {@code DockingPort} and defers processing to the
	 * {@code DockingStrategy}. This method's return value will be based upon
	 * the {@code DockingStrategy} implementation and is subject to conditions
	 * such as whether the supplied region is deemed valid, whether the
	 * {@code DockingStrategy} allows this particular {@code Dockable} to be
	 * docked into the supplied region of the specified {@code DockingPort},
	 * and so on. The {@code DockingStrategy} used is obtained by a call to
	 * {@code getDockingStrategy(Object obj)} and may be controlled via
	 * {@code setDockingStrategy(Class c, DockingStrategy strategy)}, supplying
	 * a {@code DockingPort} implementation class and a customized
	 * {@code DockingStrategy}.
	 *
	 * @param dockable the {@code Dockable} to be docked.
	 * @param port     the {@code DockingPort} into which the specified
	 *                 {@code Component} will be docked.
	 * @param region   the region into which to dock the specified {@code Dockable}
	 * @return {@code true} if the docking operation was successful,
	 * {@code false} otherwise.
	 * @see DockingStrategy#dock(Dockable, DockingPort, String)
	 * @see #getDockingStrategy(Object)
	 * @see #setDockingStrategy(Class, DockingStrategy)
	 */
	public static boolean dock(Dockable dockable, DockingPort port,
	                           String region) {
		if (dockable == null)
			return false;

		DockingStrategy strategy = getDockingStrategy(port);
		if (strategy != null) {
			return strategy.dock(dockable, port, region);
		}

		return false; // TODO think of changing it to runtime exception I
		// don't see a situation when there would be no docker.
	}

	private static Dockable resolveDockable(Component comp) {
		if (comp == null)
			return null;

		Dockable d = getDockable(comp);
		if (d == null)
			d = registerDockable(comp);
		return d;
	}

	/**
	 * Docks the specified {@code Component} relative to another already-docked
	 * {@code Component} in the CENTER region. Valid {@code Dockable} instances
	 * are looked up for both {@code Component} parameters and processing is
	 * deferred to {@code dock(Dockable dockable, Dockable parent)}. If a valid
	 * {@code Dockable} cannot be resolved for either {@code Component}, then
	 * this method returns {@code false}. The "parent" {@code Dockable} must
	 * currently be docked. If not, this method will return {@code false}.
	 * Otherwise, its parent {@code DockingPort} will be resolved and the new
	 * {@code Dockable} will be docked into the {@code DockingPort} relative to
	 * the "parent" {@code Dockable}.
	 *
	 * @param dockable the {@code Component} to be docked
	 * @param parent   the {@code Component} used as a reference point for docking
	 * @return {@code true} if the docking operation was successful;
	 * {@code false} otherwise.
	 * @see DockingManager#dock(Dockable, Dockable)
	 */
	public static boolean dock(Component dockable, Component parent) {
		return dock(resolveDockable(dockable), resolveDockable(parent));
	}

	/**
	 * Docks the specified {@code Dockable} relative to another already-docked
	 * {@code Dockable} in the CENTER region. The "parent" {@code Dockable} must
	 * currently be docked. If not, this method will return {@code false}.
	 * Otherwise, its parent {@code DockingPort} will be resolved and the new
	 * {@code Dockable} will be docked into the {@code DockingPort} relative to
	 * the "parent" {@code Dockable}. This method defers processing to
	 * {@code dock(Dockable dockable, Dockable parent, String region)} and
	 * returns {@code false} if any of the input parameters are {@code null}.
	 *
	 * @param dockable the {@code Dockable} to be docked
	 * @param parent   the {@code Dockable} used as a reference point for docking
	 * @return {@code true} if the docking operation was successful;
	 * {@code false} otherwise.
	 * @see #dock(Dockable, Dockable, String)
	 */
	public static boolean dock(Dockable dockable, Dockable parent) {
		return dock(dockable, parent, CENTER_REGION);
	}

	/**
	 * Docks the specified {@code Component} relative to another already-docked
	 * {@code Component} in the specified region. Valid {@code Dockable}
	 * instances will be looked up for each of the {@code Component} parameters.
	 * If a valid {@code Dockable} is not found for either {@code Component},
	 * then this method returns {@code false}. The "parent" {@code Dockable}
	 * must currently be docked. If not, this method will return {@code false}.
	 * Otherwise, its parent {@code DockingPort} will be resolved and the new
	 * {@code Dockable} will be docked into the {@code DockingPort} relative to
	 * the "parent" {@code Dockable}. This method defers processing to
	 * {@code dock(Component dockable, Component parent, String region, float proportion)}
	 * and returns {@code false} if any of the input parameters are {@code null}.
	 * If the specified region is other than CENTER, then a split layout should
	 * result. This method supplies a split proportion of 0.5F, resulting in
	 * equal distribution of space between the dockable and parent parameters if
	 * docking is successful.
	 *
	 * @param dockable the {@code Component} to be docked
	 * @param parent   the {@code Component} used as a reference point for docking
	 * @param region   the relative docking region into which {@code dockable} will
	 *                 be docked
	 * @return {@code true} if the docking operation was successful;
	 * {@code false} otherwise.
	 * @see #dock(Component, Component, String, float)
	 */
	public static boolean dock(Component dockable, Component parent,
	                           String region) {
		return dock(dockable, parent, region, 0.5f);
	}

	/**
	 * Docks the specified {@code Dockable} relative to another already-docked
	 * {@code Dockable} in the specified region. The "parent" {@code Dockable}
	 * must currently be docked. If not, this method will return {@code false}.
	 * Otherwise, its parent {@code DockingPort} will be resolved and the new
	 * {@code Dockable} will be docked into the {@code DockingPort} relative to
	 * the "parent" {@code Dockable}. This method defers processing to
	 * {@code dock(Dockable dockable, Dockable parent, String region, float proportion)}
	 * and returns {@code false} if any of the input parameters are {@code null}.
	 * If the specified region is other than CENTER, then a split layout should
	 * result. This method supplies a split proportion of 0.5F, resulting in
	 * equal distribution of space between the dockable and parent parameters if
	 * docking is successful.
	 *
	 * @param dockable the {@code Dockable} to be docked
	 * @param parent   the {@code Dockable} used as a reference point for docking
	 * @param region   the docking region into which {@code dockable} will be docked
	 * @return {@code true} if the docking operation was successful;
	 * {@code false} otherwise.
	 * @see #dock(Dockable, Dockable, String, float)
	 */
	public static boolean dock(Dockable dockable, Dockable parent, String region) {
		return dock(dockable, parent, region, 0.5f);
	}

	/**
	 * Docks the specified {@code Component} relative to another already-docked
	 * {@code Component} in the specified region with the specified split
	 * proportion. Valid {@code Dockable} instances will be looked up for each
	 * of the {@code Component} parameters. If a valid {@code Dockable} is not
	 * found for either {@code Component}, then this method returns
	 * {@code false}. The "parent" {@code Dockable} must currently be docked.
	 * If not, this method will return {@code false}. Otherwise, its parent
	 * {@code DockingPort} will be resolved and the new {@code Dockable} will be
	 * docked into the {@code DockingPort} relative to the "parent"
	 * {@code Dockable}. If the specified region is CENTER, then the
	 * {@code proportion} parameter is ignored. Otherwise, a split layout should
	 * result with the proportional space specified in the {@code proportion}
	 * parameter allotted to the {@code dockable} argument. This method defers
	 * processing to
	 * {@code dock(Dockable dockable, Dockable parent, String region, float proportion)}.
	 *
	 * @param dockable   the {@code Component} to be docked
	 * @param parent     the {@code Component} used as a reference point for docking
	 * @param region     the relative docking region into which {@code dockable} will
	 *                   be docked
	 * @param proportion the proportional space to allot the {@code dockable} argument
	 *                   if the docking operation results in a split layout.
	 * @return {@code true} if the docking operation was successful;
	 * {@code false} otherwise.
	 */
	public static boolean dock(Component dockable, Component parent,
	                           String region, float proportion) {
		Dockable newDockable = resolveDockable(dockable);
		Dockable parentDockable = resolveDockable(parent);
		return dock(newDockable, parentDockable, region, proportion);
	}

	/**
	 * Docks the specified {@code Dockable} relative to another already-docked
	 * {@code Dockable} in the specified region with the specified split
	 * proportion. The "parent" {@code Dockable} must currently be docked. If
	 * not, this method will return {@code false}. Otherwise, its parent
	 * {@code DockingPort} will be resolved and the new {@code Dockable} will be
	 * docked into the {@code DockingPort} relative to the "parent"
	 * {@code Dockable}. If the specified region is CENTER, then the
	 * {@code proportion} parameter is ignored. Otherwise, a split layout should
	 * result with the proportional space specified in the {@code proportion}
	 * parameter allotted to the {@code dockable} argument.
	 *
	 * @param dockable   the {@code Dockable} to be docked
	 * @param parent     the {@code Dockable} used as a reference point for docking
	 * @param region     the docking region into which {@code dockable} will be docked
	 * @param proportion the proportional space to allot the {@code dockable} argument
	 *                   if the docking operation results in a split layout.
	 * @return {@code true} if the docking operation was successful;
	 * {@code false} otherwise.
	 */
	public static boolean dock(Dockable dockable, Dockable parent,
	                           String region, float proportion) {
		return DockingUtility
				.dockRelative(dockable, parent, region, proportion);
	}

	private static DockingStrategy findDockingStrategy(Dockable dockable) {
		DockingPort port = dockable.getDockingPort();
		DockingStrategy strategy = port == null ? null : port
				.getDockingStrategy();
		if (strategy == null) {
			DockingManager mgr = getDockingManager();
			strategy = mgr == null ? null : mgr.defaultDocker;
		}
		return strategy;
	}

	/**
	 * Indicates whether the specified {@code Component} is currently docked.
	 * This method looks up a parent {@code DockingPort} for the specified
	 * {@code Component} via a call to
	 * {@code getDockingPort(Component dockable)}. This method returns
	 * {@code true} if a parent {@code DockingPort} is found and {@code false}
	 * if no parent {@code DockingPort} is present. This method returns
	 * {@code false} if the {@code Component} parameter is {@code null}.
	 *
	 * @param component the {@code Component} whose docking status is to be examined
	 * @return {@code true} if the {@code Component} is currently docked;
	 * otherwise {@code false}.
	 */
	public static boolean isDocked(Component component) {
		return getDockingPort(component) != null;
	}

	/**
	 * Indicates whether the specified {@code Dockable} is currently docked.
	 * This method looks up a parent {@code DockingPort} for the specified
	 * {@code Dockable} via a call to {@code getDockingPort(Dockable dockable)}.
	 * This method returns {@code true} if a parent {@code DockingPort} is found
	 * and {@code false} if no parent {@code DockingPort} is present. This
	 * method returns {@code false} if the {@code Dockable} parameter is
	 * {@code null}.
	 *
	 * @param dockable the {@code Dockable} whose docking status is to be examined
	 * @return {@code true} if the {@code Dockable} is currently docked;
	 * otherwise {@code false}.
	 */
	public static boolean isDocked(Dockable dockable) {
		return getDockingPort(dockable) != null;
	}

	/**
	 * Checks whether a supplied {@code Dockable} is docked within a supplied
	 * {@code DockingPort} instance. Returns {@code true} if the
	 * {@code DockingPort} contains the specified {@code Dockable};
	 * {@code false} otherwise. This method returns {@code false} if either of
	 * the input parameters are {@code null}.
	 *
	 * @param dockingPort the {@code DockingPort} to be tested
	 * @param dockable    the {@code Dockable} instance to be examined
	 * @return {@code true} if the supplied {@code DockingPort} contains the
	 * specified {@code Dockable}; {@code false} otherwise.
	 */
	public static boolean isDocked(DockingPort dockingPort, Dockable dockable) {
		return dockingPort == null || dockable == null ? false : dockingPort
				.isParentDockingPort(dockable.getComponent());
	}

	public static boolean isMinimized(Dockable dockable) {
		return DockingUtility.isMinimized(dockable);
	}

	/**
	 * Indicates whether global floating support is currently enabled. Defers
	 * processing to {@code FloatPolicyManager.isGlobalFloatingEnabled()}.
	 *
	 * @return {@code true} if global floating support is enabled, {@code false}
	 * otherwise.
	 * @see FloatPolicyManager#isGlobalFloatingEnabled()
	 */
	public static boolean isFloatingEnabled() {
		return FloatPolicyManager.isGlobalFloatingEnabled();
	}

	/**
	 * Indicates whether tabbed layouts are supported by default for
	 * {@code DockingPorts} with a single {@code Dockable} in the CENTER region.
	 * This is a global default setting and applies to any <cod>DockingPort}
	 * that does not have a specific contradictory local setting.
	 * <p>
	 * This method defers processing to
	 * {@code org.flexdock.docking.props.PropertyManager.getDockingPortRoot()}.
	 * As such, there are multiple "scopes" at which this property may be
	 * overridden.
	 *
	 * @return {@code true} if the default setting for {@code DockingPorts}
	 * allows a tabbed layout for a single {@code Dockable} in the
	 * CENTER region; {@code false} otherwise.
	 * @see PropertyManager#getDockingPortRoot()
	 * @see org.flexdock.docking.props.DockingPortPropertySet#isSingleTabsAllowed()
	 */
	public static boolean isSingleTabsAllowed() {
		return PropertyManager.getDockingPortRoot().isSingleTabsAllowed()
				.booleanValue();
	}

	/**
	 * Indicates whether the supplied parameter is considered a valid docking
	 * region. Valid values are those defined in {@code DockingConstants} and
	 * include {@code NORTH_REGION}, {@code SOUTH_REGION}, {@code EAST_REGION},
	 * {@code WEST_REGION}, and {@code CENTER_REGION}. This method returns
	 * {@code true} if the supplied parameter is equal to one of these values.
	 *
	 * @param region the region value to be tested
	 * @return {@code true} if the supplied parameter is a valid docking region;
	 * {@code false} otherwise.
	 */
	public static boolean isValidDockingRegion(String region) {
		return CENTER_REGION.equals(region) || NORTH_REGION.equals(region)
				|| SOUTH_REGION.equals(region) || EAST_REGION.equals(region)
				|| WEST_REGION.equals(region);
	}

	private static void updateDragListeners(Component dragSrc,
	                                        DragManager listener) {
		MouseMotionListener motionListener = null;
		EventListener[] listeners = dragSrc.getMouseMotionListeners();
		for (int i = 0; i < listeners.length; i++) {
			if (listeners[i] instanceof DragManager) {
				motionListener = (MouseMotionListener) listeners[i];
				break;
			}
		}
		if (motionListener != listener) {
			if (motionListener != null)
				dragSrc.removeMouseMotionListener(motionListener);
			dragSrc.addMouseMotionListener(listener);
		}

		MouseListener mouseListener = null;
		listeners = dragSrc.getMouseListeners();
		for (int i = 0; i < listeners.length; i++) {
			if (listeners[i] instanceof DragManager) {
				mouseListener = (MouseListener) listeners[i];
				break;
			}
		}
		if (mouseListener != listener) {
			if (mouseListener != null)
				dragSrc.removeMouseListener(mouseListener);
			dragSrc.addMouseListener(listener);
		}
	}

	/**
	 * Creates, registers, and returns a {@code Dockable} for the specified
	 * {@code Component}. If the specified {@code Component} implements the
	 * {@code Dockable} interface, then this method dispatches to
	 * {@code registerDockable(Dockable dockable)}. Otherwise, this method
	 * dispatches to {@code registerDockable(Component comp, String tabText)}.
	 * <p>
	 * This method attempts to resolve an appropriate value for {@code tabText}
	 * by calling {@code getName()} on the specified {@code Component}. If the
	 * resolved value is {@code null} or empty, then the value {@code "null"} is
	 * used.
	 * <p>
	 * If {@code comp} is {@code null}, no exception is thrown and no action is
	 * performed.
	 *
	 * @param comp the target component for the {@code Dockable}.
	 * @return the {@code Dockable} that has been registered for the supplied
	 * {@code Component}
	 * @see #registerDockable(Dockable)
	 * @see #registerDockable(Component, String)
	 */
	public static Dockable registerDockable(Component comp) {
		if (comp == null)
			return null;

		if (comp instanceof Dockable)
			return registerDockable((Dockable) comp);

		return registerDockable(comp, null, null);
	}

	private static String determineTabText(Component comp, String persistId) {
		String tabText = null;
		// if 'comp' is a DockingStub, then we may be able to
		// pull the tab text from it
		if (comp instanceof DockingStub) {
			tabText = ((DockingStub) comp).getTabText();
		} else {
			// if we can find an adapter mapping, then try to pull
			// the tab text from there
			DockingAdapter adapter = AdapterFactory.getAdapter(comp);
			if (adapter != null)
				tabText = adapter.getTabText();
		}

		// if 'comp' wasn't a DockingStub, or the stub returned a null tabText,
		// then try the component name
		if (tabText == null)
			tabText = comp.getName();

		// if tabText is still null, then use the persistentId
		if (tabText == null)
			tabText = persistId;

		// get rid of null and empty cases. use the string "null" if nothing
		// else can be found
		tabText = tabText == null ? "null" : tabText.trim();
		if (tabText.length() == 0)
			tabText = "null";

		return tabText;
	}

	/**
	 * Creates a {@code Dockable} for the specified {@code Component} and
	 * dispatches to {@code registerDockable(Dockable init)}. If {@code comp}
	 * is {@code null}, no exception is thrown and no action is performed.
	 *
	 * @param comp    the target component for the Dockable, both drag-starter and
	 *                docking source
	 * @param tabText the description of the docking source. Used as the tab-title
	 *                of docked in a tabbed pane
	 * @return the {@code Dockable} that has been registered for the supplied
	 * {@code Component}
	 * @see #registerDockable(Dockable)
	 */
	public static Dockable registerDockable(Component comp, String tabText) {
		return registerDockable(comp, tabText, null);
	}

	private static Dockable registerDockable(Component comp, String tabText,
	                                         String dockingId) {
		if (comp == null)
			return null;

		if (tabText == null)
			tabText = determineTabText(comp, dockingId);

		Dockable dockable = getDockableForComponent(comp, tabText, dockingId);
		return registerDockable(dockable);
	}

	/**
	 * Registers and initializes the specified {@code Dockable}. All
	 * {@code Dockables} managed by the framework must, at some point, be
	 * registered via this method. This method adds the {@code Dockable} to the
	 * internal registry, allowing querying by ID and {@code Component}. Drag
	 * listeners are added to the {@code Dockable} to enable drag-n-drop docking
	 * support. Docking properties are also initialized for the {@code Dockable}.
	 * This method fires a {@code RegistrationEvent} once the {@code Dockable}
	 * has been registered. If the {@code Dockable} is {@code null}, no
	 * {@code Exception} is thrown and no action is taken. The {@code Dockable}
	 * returned by this method will be the same object passed in as an argument.
	 *
	 * @param dockable the Dockable that is being registered.
	 * @return the {@code Dockable} that has been registered.
	 * @see org.flexdock.event.RegistrationEvent
	 */
	public static Dockable registerDockable(Dockable dockable) {
		if (dockable == null || dockable.getComponent() == null
				|| dockable.getDragSources() == null)
			return null;

		if (dockable.getPersistentId() == null)
			throw new IllegalArgumentException(
					"Dockable must have a non-null persistent ID.");

		DOCKABLES_BY_COMPONENT.put(dockable.getComponent(), dockable);

		// flag the component as dockable, in case it doesn't
		// implement the interface directly
		Component c = dockable.getComponent();
		SwingUtility.putClientProperty(c, Dockable.DOCKABLE_INDICATOR,
				Boolean.TRUE);

		// add drag listeners
		updateDragListeners(dockable);

		// add the dockable as its own listener
		dockable.addDockingListener(dockable);

		// cache the dockable by ID
		DOCKABLES_BY_ID.put(dockable.getPersistentId(), dockable);

		// make sure we have docking-properties initialized (must come after
		// ID-caching)
		DockablePropertySet props = PropertyManager
				.getDockablePropertySet(dockable);

		// dispatch a registration event
		EventManager.dispatch(new RegistrationEvent(dockable,
				DockingManager.SINGLETON, true));

		// return the dockable
		return dockable;
	}

	public static void unregisterDockable(Component comp) {
		Dockable dockable = getDockable(comp);
		unregisterDockable(dockable);
	}

	public static void unregisterDockable(String dockingId) {
		Dockable dockable = getDockableImpl(dockingId);
		unregisterDockable(dockable);
	}

	public static void unregisterDockable(Dockable dockable) {
		if (dockable == null)
			return;

		synchronized (DOCKABLES_BY_COMPONENT) {
			DOCKABLES_BY_COMPONENT.remove(dockable.getComponent());
		}

		// flag the component as dockable, in case it doesn't
		// implement the interface directly
		Component c = dockable.getComponent();
		SwingUtility.removeClientProperty(c, Dockable.DOCKABLE_INDICATOR);

		// remove the drag listeners
		removeDragListeners(dockable);

		// remove the dockable as its own listener
		dockable.removeDockingListener(dockable);

		// unlink the propertySet
		PropertyManager.removePropertySet(dockable);

		// remove the dockable by ID
		synchronized (DOCKABLES_BY_ID) {
			DOCKABLES_BY_ID.remove(dockable.getPersistentId());
		}

		// dispatch a registration event
		EventManager.dispatch(new RegistrationEvent(dockable,
				DockingManager.SINGLETON, false));
	}

	/**
	 * Removes the event listeners that manage drag-n-drop docking operations
	 * from the specified {@code Component}. If the specific listeners are not
	 * present, then no action is taken. Drag listeners used by the docking
	 * system are of type {@code org.flexdock.docking.drag.DragManager}.
	 *
	 * @param comp the {@code Component} from which to remove drag listeners.
	 * @see DragManager
	 */
	public static void removeDragListeners(Component comp) {
		if (comp == null)
			return;

		MouseMotionListener motionListener = null;
		EventListener[] listeners = comp.getMouseMotionListeners();
		for (int i = 0; i < listeners.length; i++) {
			if (listeners[i] instanceof DragManager) {
				motionListener = (MouseMotionListener) listeners[i];
				break;
			}
		}
		if (motionListener != null) {
			comp.removeMouseMotionListener(motionListener);
		}

		MouseListener mouseListener = null;
		listeners = comp.getMouseListeners();
		for (int i = 0; i < listeners.length; i++) {
			if (listeners[i] instanceof DragManager) {
				mouseListener = (MouseListener) listeners[i];
				break;
			}
		}
		if (mouseListener != null) {
			comp.removeMouseListener(mouseListener);
		}
	}

	/**
	 * Displays the specified {@code Dockable} in the application's docking
	 * layout. If the {@code Dockable} has not previously been docked, a
	 * suitable location is determined within the layout and the
	 * {@code Dockable} is docked to that location. If the {@code Dockable} has
	 * previously been docked within the layout and subsequently removed, as
	 * with a call to {@code DockingManager.close()}, the {@code Dockable} will
	 * be restored to its prior state within the layout. This method defers
	 * processing to the {@code display(Dockable dockable)} method for the
	 * currently installed {@code org.flexdock.docking.state.LayoutManager}.
	 * The {@code LayoutManager} implementation is responsible for handling the
	 * semantics of determining an initial docking location or restoring a
	 * {@code Dockable} to its previous layout state. If the {@code Dockable}
	 * parameter is {@code null}, no {@code Exception} is thrown and no action
	 * is taken.
	 *
	 * @param dockable the {@code Dockable} to be displayed.
	 * @return {@code true} if the {@code Dockable} was successfully displayed;
	 * {@code false} otherwise.
	 * @see #getLayoutManager()
	 * @see LayoutManager#display(Dockable)
	 */
	public static boolean display(Dockable dockable) {
		return getLayoutManager().display(dockable);
	}

	/**
	 * Displays the {@code Dockable} with the specified ID within the
	 * application's docking layout. A valid {@code Dockable} is looked up for
	 * the supplied ID. If none is found, this method returns {@code false}.
	 * Otherwise, processing is dispatched to {@code display(Dockable dockable)}.
	 * If the {@code Dockable} has not previously been docked, a suitable
	 * location is determined within the layout and the {@code Dockable} is
	 * docked to that location. If the {@code Dockable} has previously been
	 * docked within the layout and subsequently removed, as with a call to
	 * {@code DockingManager.close()}, the {@code Dockable} will be restored to
	 * its prior state within the layout. This method defers processing to the
	 * {@code display(Dockable dockable)} method for the currently installed
	 * {@code org.flexdock.docking.state.LayoutManager}. The
	 * {@code LayoutManager} implementation is responsible for handling the
	 * semantics of determining an initial docking location or restoring a
	 * {@code Dockable} to its previous layout state. If the {@code Dockable}
	 * parameter is {@code null}, no {@code Exception} is thrown and no action
	 * is taken.
	 *
	 * @param dockable the ID of the {@code Dockable} to be displayed.
	 * @return {@code true} if the {@code Dockable} was successfully displayed;
	 * {@code false} otherwise.
	 * @see #display(Dockable)
	 * @see #getLayoutManager()
	 * @see LayoutManager#display(Dockable)
	 */
	public static boolean display(String dockable) {
		return getLayoutManager().display(getDockable(dockable));
	}

	private static String generatePersistentId(Object obj) {
		return generatePersistentId(obj, null);
	}

	private static String generatePersistentId(Object obj, String desiredId) {
		if (obj == null)
			return null;

		synchronized (persistentIdLock) {
			String pId = desiredId == null ? obj.getClass().getName()
					: desiredId;
			StringBuffer baseId = new StringBuffer(pId);
			for (int i = 1; hasRegisteredDockableId(pId); i++) {
				baseId.append("_").append(i);
				pId = baseId.toString();
			}
			return pId;
		}
	}

	private static boolean hasRegisteredDockableId(String id) {
		return DOCKABLES_BY_ID.containsKey(id);
	}

	/**
	 * Returns the {@code DockingStrategy} associated with the {@code Class} of
	 * the {@code Object} parameter. This method returns {@code null} if the
	 * parameter is {@code null}. Otherwise, the method retrieves the
	 * {@code Object's} {@code Class} and dispatches to
	 * {@code getDockingStrategy(Class classKey)}.
	 * <p>
	 * {@code DockingStrategy} association follows a strict inheritance chain
	 * using {@code org.flexdock.util.ClassMapping}. If a mapping for
	 * {@code obj.getClass()} is not found, then the superclass is tested, and
	 * so on until {@code java.lang.Object} is reached. Thus, if a
	 * {@code DockingStrategy} mapping of {@code Foo} exists for class
	 * {@code Bar}, and class {@code Baz} extends {@code Bar}, then calling
	 * this method for an instance of {@code Baz} will return an instance of
	 * {@code Foo}. The inheritance chain is <i>strict</i> in the sense that
	 * only superclasses are checked. Implemented interfaces are ignored.
	 * <p>
	 * If a class association is never found, then an instance of
	 * {@code DefaultDockingStrategy} is returned.
	 *
	 * @param obj the object whose {@code DockingStrategy} association we wish
	 *            to test
	 * @return the {@code DockingStrategy} associated with the {@code Class}
	 * type of the {@code Object} parameter.
	 * @see #getDockingStrategy(Class)
	 * @see #setDockingStrategy(Class, DockingStrategy)
	 * @see ClassMapping#getClassInstance(Class)
	 */
	public static DockingStrategy getDockingStrategy(Object obj) {
		Class key = obj == null ? null : obj.getClass();
		return getDockingStrategy(key);
	}

	/**
	 * Returns the {@code DockingStrategy} associated with specified
	 * {@code Class}. This method returns {@code null} if the parameter is
	 * {@code null}.
	 * <p>
	 * {@code DockingStrategy} association follows a strict inheritance chain
	 * using {@code org.flexdock.util.ClassMapping}. If a mapping for
	 * {@code classKey} is not found, then the superclass is tested, and so on
	 * until {@code java.lang.Object} is reached. Thus, if a
	 * {@code DockingStrategy} mapping of {@code Foo} exists for class
	 * {@code Bar}, and class {@code Baz} extends {@code Bar}, then calling
	 * this method for class {@code Baz} will return an instance of {@code Foo}.
	 * The inheritance chain is <i>strict</i> in the sense that only
	 * superclasses are checked. Implemented interfaces are ignored.
	 * <p>
	 * If a class association is never found, then an instance of
	 * {@code DefaultDockingStrategy} is returned.
	 *
	 * @param classKey the {@code Class} whose {@code DockingStrategy} association we
	 *                 wish to test
	 * @return the {@code DockingStrategy} associated with the specified
	 * {@code Class}.
	 * @see #setDockingStrategy(Class, DockingStrategy)
	 * @see ClassMapping#getClassInstance(Class)
	 */
	public static DockingStrategy getDockingStrategy(Class classKey) {
		DockingStrategy strategy = (DockingStrategy) DOCKING_STRATEGIES
				.getClassInstance(classKey);
		return strategy;
	}

	/**
	 * Returns an array of {@code RootWindows} known to the docking framework
	 * that contain {@code DockingPorts}. Any {@code Frame}, {@code Applet},
	 * {@code Dialog}, or {@code Window} that has a {@code DockingPort} added
	 * as a descendent {@code Component} will automatically have an
	 * {@code org.flexdock.util.RootWindow} wrapper instance associated with it.
	 * This method will return an array of all known RootWindows that contain
	 * {@code DockingPorts}. Ordering of the array may be based off of a
	 * {@code java.util.Set} and is <b>not</b> guaranteed.
	 *
	 * @return an array of all known {@code RootWindows} that contain
	 * {@code DockingPorts}
	 * @see RootWindow
	 * @see DockingPortTracker#getDockingWindows()
	 */
	public static RootWindow[] getDockingWindows() {
		Set windowSet = DockingPortTracker.getDockingWindows();
		return windowSet == null ? new RootWindow[0] : (RootWindow[]) windowSet
				.toArray(new RootWindow[0]);
	}

	/**
	 * Returns the {@code DockingPort} with the specified ID. If the
	 * {@code portId} parameter is {@code null}, or a {@code DockingPort} with
	 * the specified ID is not found, a {@code null} reference is returned. This
	 * method internally dispatches to
	 * {@code org.flexdock.docking.event.hierarchy.DockingPortTracker.findById(String portId)}.
	 * {@code portId} should match the value returned by a {@code DockingPort's}
	 * {@code getPersistentId()} method.
	 *
	 * @param portId the ID of the {@code DockingPort} to be looked up
	 * @return the {@code DockingPort} with the specified ID
	 * @see DockingPort#getPersistentId()
	 * @see DockingPortTracker#findById(String)
	 */
	public static DockingPort getDockingPort(String portId) {
		return DockingPortTracker.findById(portId);
	}

	/**
	 * Returns the "main" {@code DockingPort} within the application window
	 * containing the specified {@code Component}. Just as desktop applications
	 * will tend to have a "main" application window, perhaps surrounded with
	 * satellite windows or dialogs, the "main" {@code DockingPort} within a
	 * given window will be considered by the application developer to contain
	 * the primary docking layout used by the enclosing window.
	 * <p>
	 * The {@code Component} parameter may or may not be a root window
	 * container. If not, the ancestor window of {@code comp} is determined and
	 * a set of docking ports encapsulated by a {@code RootDockingPortInfo}
	 * instance is returned by a call to
	 * {@code getRootDockingPortInfo(Component comp)}. The resolved
	 * {@code RootDockingPortInfo} instance's main {@code DockingPort} is
	 * returned via its method {@code getMainPort()}.
	 * <p>
	 * By default, the "main" {@code DockingPort} assigned to any
	 * {@code RootDockingPortInfo} instance associated with a window will happen
	 * to be the first root {@code DockingPort} detected for that window. In
	 * essence, the default settings make this method identical to
	 * {@code getRootDockingPort(Component comp)}. This, however, may be
	 * altered by {@code RootDockingPortInfo's}
	 * {@code setMainPort(String portId)} method based upon the needs of the
	 * application developer. In contrast,
	 * {@code getMainDockingPort(Component comp)} will always return the first
	 * root {@code DockingPort} found within a window.
	 * <p>
	 * If {@code comp} is {@code null} or the root window cannot be resolved,
	 * then this method returns a {@code null} reference. A {@code null}
	 * reference is also returned if the root window does not contain any
	 * {@code DockingPorts}.
	 *
	 * @param comp the {@code Component} whose root window will be checked for a
	 *             main {@code DockingPort}
	 * @return the main {@code DockingPort} within the root window that contains
	 * {@code comp}
	 * @see #getRootDockingPortInfo(Component)
	 * @see #getRootDockingPort(Component)
	 * @see DockingPortTracker#getRootDockingPortInfo(Component)
	 * @see RootDockingPortInfo#getMainPort()
	 * @see RootDockingPortInfo#setMainPort(String)
	 */
	public static DockingPort getMainDockingPort(Component comp) {
		RootDockingPortInfo info = getRootDockingPortInfo(comp);
		return info == null ? null : info.getMainPort();
	}

	/**
	 * Returns the first root {@code DockingPort} found within the application
	 * window containing the specified {@code Component}. A "root"
	 * {@code DockingPort} is a {@code DockingPort} embedded within a
	 * window/frame/applet/dialog that is not nested within any other parent
	 * {@code DockingPorts}. The {@code Component} parameter may or may not be
	 * a root window container itself. If not, the root window containing
	 * {@code comp} is resolved and the first root {@code DockingPort} found
	 * within it is returned. This method defers actual processing to
	 * {@code org.flexdock.docking.event.hierarchy.DockingPortTracker.findByWindow(Component comp)}.
	 * <p>
	 * If {@code comp} is {@code null} or the root window cannot be resolved,
	 * then this method returns a {@code null} reference. A {@code null}
	 * reference is also returned if the root window does not contain any
	 * {@code DockingPorts}.
	 * <p>
	 * This method differs from {@code getMainDockingPort(Component comp)} in
	 * that the "main" {@code DockingPort} for a given window is configurable by
	 * the application developer, whereas this method will always return the
	 * "first" {@code DockingPort} found within the window. However, if the
	 * "main" {@code DockingPort} has not been manually configured by the
	 * application developer, then this method and
	 * {@code getMainDockingPort(Component comp)} will exhibit identical
	 * behavior.
	 *
	 * @param comp the {@code Component} whose root window will be checked for a
	 *             root {@code DockingPort}
	 * @return the first root {@code DockingPort} found within the root window
	 * that contains {@code comp}
	 * @see #getMainDockingPort(Component)
	 * @see DockingPortTracker#findByWindow(Component)
	 * @see RootDockingPortInfo
	 */
	public static DockingPort getRootDockingPort(Component comp) {
		return DockingPortTracker.findByWindow(comp);
	}

	/**
	 * Returns the {@code RootDockingPortInfo} instance associated with the root
	 * window containing the specified {@code Component}. The {@code Component}
	 * parameter may or may not be a root window container itself. If not, the
	 * root window containing {@code comp} is resolved and the
	 * {@code RootDockingPortInfo} instance associated with the window is
	 * returned. {@code RootDockingPortInfo} will contain information regarding
	 * all of the "root" {@code DockingPorts} embedded within a root window
	 * where a "root" {@code DockingPort} is any {@code DockingPort} embedded
	 * within the window that does not have any other {@code DockingPort}
	 * ancestors in it's container hierarchy.
	 * <p>
	 * If {@code comp} is {@code null} or the root window cannot be resolved,
	 * then this method returns a {@code null} reference. A {@code null}
	 * reference is also returned if the root window does not contain any
	 * {@code DockingPorts}.
	 * <p>
	 * This method dispatches internally to
	 * {@code org.flexdock.docking.event.hierarchy.DockingPortTracker.getRootDockingPortInfo(Component comp)}.
	 *
	 * @param comp the {@code Component} whose root window will be checked for an
	 *             associated {@code RootDockingPortInfo}.
	 * @return the {@code RootDockingPortInfo} instance associated with the root
	 * window containing {@code comp}.
	 * @see RootDockingPortInfo
	 * @see DockingPortTracker#getRootDockingPortInfo(Component)
	 */
	public static RootDockingPortInfo getRootDockingPortInfo(Component comp) {
		return DockingPortTracker.getRootDockingPortInfo(comp);
	}

	/**
	 * Sends the application's current layout model to external storage. This
	 * method defers processing to the currently installed
	 * {@code org.flexdock.docking.state.LayoutManager} by invoking its
	 * {@code store()} method. If there is no {@code LayoutManager} installed,
	 * then this method returns {@code false}.
	 * <p>
	 * The layout model itself, along with storage mechanism, is abstract and
	 * dependent upon the particular {@code LayoutManager} implementation. As
	 * such, it may be possible that the {@code LayoutManager} is unable to
	 * persist the current layout state for non-Exceptional reasons. This method
	 * returns {@code true} if the layout model was successfully stored and
	 * {@code false} if the layout model could not be stored under circumstances
	 * that do not generate an {@code Exception} (for instance, if there is no
	 * persistence implementation currently installed). If a problem occurs
	 * during the persistence process, an {@code IOException} is thrown.
	 *
	 * @return {@code true} if the current layout model was succesfully stored,
	 * {@code false} otherwise.
	 * @throws IOException
	 * @see #getLayoutManager()
	 * @see #setLayoutManager(LayoutManager)
	 * @see LayoutManager#store()
	 */
	public static boolean storeLayoutModel() throws IOException,
			PersistenceException {
		LayoutManager mgr = getLayoutManager();
		return mgr == null ? false : mgr.store();
	}

	/**
	 * Loads a previously stored layout model into the currently installed
	 * {@code LayoutManager}. This method defers processing to
	 * {@code loadLayoutModel(boolean restore)} with an argument of
	 * {@code false} to indicate that the stored data model should merely be
	 * loaded into memory and the {@code LayoutManager} should not attempt to
	 * subsequently restore the application view by synchronizing it against the
	 * newly loaded data model.
	 * <p>
	 * The layout model itself, along with storage mechanism, is abstract and
	 * dependent upon the particular {@code LayoutManager} implementation. As
	 * such, it may be possible that the {@code LayoutManager} is unable to load
	 * the previous layout state for non-Exceptional reasons. This method
	 * returns {@code true} if the layout model was successfully loaded and
	 * {@code false} if the layout model could not be loaded under circumstances
	 * that do not generate an {@code Exception} (for instance, if there was no
	 * previous layout model found in storage). If a problem occurs during the
	 * loading process, an {@code IOException} is thrown.
	 *
	 * @return {@code true} if the current layout model was succesfully loaded,
	 * {@code false} otherwise.
	 * @throws IOException
	 * @see #loadLayoutModel(boolean)
	 * @see LayoutManager#load()
	 */
	public static boolean loadLayoutModel() throws IOException,
			PersistenceException {
		return loadLayoutModel(false);
	}

	/**
	 * Loads a previously stored layout model into the currently installed
	 * {@code LayoutManager} and attempts to synchronize the application view
	 * with the newly loaded layout model if the {@code restore} parameter is
	 * {@code true}. If there is no currently installed {@code LayoutManager},
	 * then this method returns {@code false}. If the {@code restore} parameter
	 * is {@code true}, then this method defers processing to
	 * {@code restoreLayout(boolean loadFromStorage)} with an argument of
	 * {@code true}. Otherwise, this method defers processing to the currently
	 * installed {@code org.flexdock.docking.state.LayoutManager} by invoking
	 * its {@code load()} method.
	 * <p>
	 * The layout model itself, along with storage mechanism, is abstract and
	 * dependent upon the particular {@code LayoutManager} implementation. As
	 * such, it may be possible that the {@code LayoutManager} is unable to load
	 * the previous layout state for non-Exceptional reasons. This method
	 * returns {@code true} if the layout model was successfully loaded and
	 * {@code false} if the layout model could not be loaded under circumstances
	 * that do not generate an {@code Exception} (for instance, if there was no
	 * previous layout model found in storage). If a problem occurs during the
	 * loading process, an {@code IOException} is thrown.
	 *
	 * @return {@code true} if the current layout model was succesfully loaded,
	 * {@code false} otherwise.
	 * @throws IOException
	 * @throws PersistenceException
	 * @see #getLayoutManager()
	 * @see #setLayoutManager(LayoutManager)
	 * @see #restoreLayout(boolean)
	 * @see LayoutManager#load()
	 */
	public static boolean loadLayoutModel(boolean restore) throws IOException,
			PersistenceException {
		LayoutManager mgr = getLayoutManager();
		if (mgr == null)
			return false;

		return restore ? restoreLayout(true) : mgr.load();
	}

	/**
	 * Synchronizes the application view with the current in-memory layout
	 * model. This method defers processing to
	 * {@code restoreLayout(boolean loadFromStorage)} with an argument of
	 * {@code false}. This instructs the currently installed
	 * {@code LayoutManager} to restore the application view to match the
	 * current in-memory layout model without reloading from storage prior to
	 * restoration. This method is useful for developers who choose to construct
	 * a layout model programmatically and wish to "commit" it to the
	 * application view, restoring their own in-memory layout model rather than
	 * a model persisted in external storage.
	 * <p>
	 * If there is no {@code LayoutManager} currently installed, then this
	 * method returns {@code false}.
	 *
	 * @return {@code true} if the in-memory layout model was properly restored
	 * to the application view, {@code false} otherwise.
	 * @throws PersistenceException
	 * @see #restoreLayout(boolean)
	 * @see #getLayoutManager()
	 * @see #setLayoutManager(LayoutManager)
	 * @see LayoutManager#restore(boolean)
	 */
	public static boolean restoreLayout() {
		try {
			return restoreLayout(false);
		} catch (IOException e) {
			// shouldn't happen since we're not intending to load from storage
			System.err.println("Exception: " + e.getMessage());
			e.printStackTrace();
			return false;
		} catch (PersistenceException e) {
			// TODO Auto-generated catch block
			System.err.println("Exception: " + e.getMessage());
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * Synchronizes the application view with the current in-memory layout
	 * model. This method defers processing to the currently installed
	 * {@code org.flexdock.docking.state.LayoutManager} by invoking its
	 * {@code restore(boolean loadFromStorage)} method. If there is no
	 * {@code LayoutManager} currently installed, then this method returns
	 * {@code false}.
	 * <p>
	 * If the {@code loadFromStorage} parameter is {@code true}, then the
	 * {@code LayoutManager} is instructed to load any persisted layout model
	 * from external storage into memory before synchronizing the application
	 * view. If a problem occurs while loading from exernal storage, this method
	 * throws an {@code IOException}.
	 *
	 * @param loadFromStorage instructs whether to load any layout model from external
	 *                        storage into memory before synchronizing the application view.
	 * @return {@code true} if the in-memory layout model was properly restored
	 * to the application view, {@code false} otherwise.
	 * @throws PersistenceException
	 * @see #getLayoutManager()
	 * @see #setLayoutManager(LayoutManager)
	 * @see LayoutManager#restore(boolean)
	 */
	public static boolean restoreLayout(boolean loadFromStorage)
			throws IOException, PersistenceException {
		LayoutManager mgr = getLayoutManager();
		return mgr == null ? false : mgr.restore(loadFromStorage);
	}

	private static Dockable loadAndRegister(String id) {
		DockableFactory factory = id == null ? null
				: getDockingManager().dockableFactory;
		if (factory == null)
			return null;

		// the getDockableComponent() implementation may or may not
		// automatically register a dockable before returning.

		// first, try to get a Dockable from the factory
		Dockable dockable = factory.getDockable(id);
		if (dockable != null) {
			// check to see if the dockable is already registered.
			Dockable tmp = getDockableImpl(dockable.getPersistentId());
			if (tmp == null)
				registerDockable(dockable);
		}
		// if we couldn't find a dockable from the factory, then try getting
		// a component.
		else {
			Component comp = factory.getDockableComponent(id);
			// we already weren't able to get a Dockable from the factory. If
			// we couldn't get a Component either, then give up.
			if (comp == null)
				return null;

			// if the newly created dockable has not yet been registered,
			// then register it.
			dockable = getDockable(comp);
			if (dockable == null) {
				dockable = registerDockable(comp, null, id);
			}
		}

		return dockable;
	}

	private static Dockable getDragInitiator(Component c) {
		return getDockableForComponent(c, null, null);
	}

	private static Dockable getDockableForComponent(Component c, String desc,
	                                                String dockingId) {
		if (c == null)
			return null;

		// return the dockable if it has already been registered
		Dockable dockable = getDockable(c);
		if (dockable != null)
			return dockable;

		// if we need to create a dockable, first try to do it with an adapter
		DockingAdapter adapter = AdapterFactory.getAdapter(c);
		if (adapter != null) {
			dockable = DockableComponentWrapper.create(adapter);
		}

		// if we weren't able to create from an adapter, then create the
		// dockable manually
		if (dockable == null) {
			if (c instanceof DockingStub) {
				dockable = DockableComponentWrapper.create((DockingStub) c);
			} else {
				String persistentId = dockingId == null ? generatePersistentId(c)
						: dockingId;
				dockable = DockableComponentWrapper.create(c, persistentId,
						desc);
			}
		}

		// make sure the specified description is applied
		if (desc != null)
			dockable.getDockingProperties().setDockableDesc(desc);

		// cache the dockable for future use
		DOCKABLES_BY_COMPONENT.put(c, dockable);

		// now we can return
		return dockable;
	}

	/**
	 * Returns the {@code DockingPort} that contains the specified
	 * {@code Component}. If the {@code Component} is {@code null}, then a
	 * {@code null} reference is returned.
	 * <p>
	 * This method will only return the immediate parent {@code DockingPort} of
	 * the specified {@code Component} This means that the {@code DockingPort}
	 * returned by this method will not only be an ancestor {@code Container} of
	 * the specified {@code Component}, but invoking its
	 * {@code isParentDockingPort(Component comp)} with the specified
	 * {@code Component} will also return {@code true}. If both of these
	 * conditions cannot be satisfied, then this method returns a {@code null}
	 * reference.
	 *
	 * @param dockable the {@code Component} whose parent {@code DockingPort} is to
	 *                 be returned.
	 * @return the imediate parent {@code DockingPort} that contains the
	 * specified {@code Component}.
	 */
	public static DockingPort getDockingPort(Component dockable) {
		return DockingUtility.getParentDockingPort(dockable);
	}

	/**
	 * Returns the {@code DockingPort} that contains the specified
	 * {@code Dockable}. If the {@code Dockable} is {@code null}, then a
	 * {@code null} reference is returned.
	 * <p>
	 * This method will only return the immediate parent {@code DockingPort} of
	 * the specified {@code Dockable} This means that a check is performed for
	 * the {@code Component} returned by the {@code Dockable's}
	 * {@code getComponent()} method. The {@code DockingPort} returned by this
	 * method will not only be an ancestor {@code Container} of this
	 * {@code Component}, but invoking the {@code DockingPort's}
	 * {@code isParentDockingPort(Component comp)} with the this
	 * {@code Component} will also return {@code true}. If both of these
	 * conditions cannot be satisfied, then this method returns a {@code null}
	 * reference.
	 *
	 * @param dockable the {@code Dockable} whose parent {@code DockingPort} is to be
	 *                 returned.
	 * @return the imediate parent {@code DockingPort} that contains the
	 * specified {@code Dockable}.
	 */
	public static DockingPort getDockingPort(Dockable dockable) {
		return DockingUtility.getParentDockingPort(dockable);
	}

	/**
	 * Returns the {@code Dockable} instance that models the specified
	 * {@code Component}. The {@code Dockable} returned by this method will
	 * return a reference to {@code comp} when its {@code getComponent()} method
	 * is called. If {@code comp} is {@code null}, then this method will return
	 * a {@code null} reference.
	 * <p>
	 * The association between {@code Dockable} and {@code Component} is
	 * established internally during {@code registerDockable(Dockable dockable)}.
	 * Thus, {@code registerDockable(Dockable dockable)} must have been called
	 * previously for a mapping to be found and a {@code Dockable} to be
	 * returned by this method. If no mapping is found for the specified
	 * {@code Component}, then this method returns a {@code null} reference.
	 *
	 * @param comp the {@code Component} whose {@code Dockable} instance is to be
	 *             returned.
	 * @return the {@code Dockable} that models the specified {@code Component}
	 * @see #registerDockable(Dockable)
	 * @see Dockable#getComponent()
	 */
	public static Dockable getDockable(Component comp) {
		return comp == null ? null : (Dockable) DOCKABLES_BY_COMPONENT
				.get(comp);
	}

	/**
	 * Returns the {@code Dockable} instance with the specified ID. The
	 * {@code Dockable} returned by this method will return a String equal
	 * {@code id} when its {@code getPersistentId()} method is called. If
	 * {@code id} is {@code null}, then this method will return a {@code null}
	 * reference.
	 * <p>
	 * The association between {@code Dockable} and {@code id} is established
	 * internally during {@code registerDockable(Dockable dockable)}. Thus,
	 * {@code registerDockable(Dockable dockable)} must have been called
	 * previously for a mapping to be found and a {@code Dockable} to be
	 * returned by this method. If no mapping is found for the specified
	 * {@code id}, then this method returns a {@code null} reference.
	 *
	 * @param id the persistent ID of the {@code Dockable} instance is to be
	 *           returned.
	 * @return the {@code Dockable} that has the specified perstent ID.
	 * @see #registerDockable(Dockable)
	 * @see Dockable#getPersistentId()
	 */
	public static Dockable getDockable(String id) {
		if (id == null)
			return null;

		Dockable dockable = getDockableImpl(id);
		if (dockable == null)
			dockable = loadAndRegister(id);
		return dockable;
	}

	private static Dockable getDockableImpl(String id) {
		synchronized (DOCKABLES_BY_ID) {
			return id == null ? null : (Dockable) DOCKABLES_BY_ID.get(id);
		}
	}

	/**
	 * Returns a {@code Set} of {@code String} IDs for all {@code Dockables}
	 * registered with the framework. The IDs returned by this method will
	 * correspond to the values returned for the {@code getPersistentId()}
	 * method for each {@code Dockable} registered with the framework.
	 * <p>
	 * {@code Dockable} IDs are cached during
	 * {@code registerDockable(Dockable dockable)}. Thus, for an ID to appear
	 * within the {@code Set} returned by this method, the corresponding
	 * {@code Dockable} must have first been registered via
	 * {@code registerDockable(Dockable dockable)}.
	 * <p>
	 * If no {@code Dockables} have been registered with the framework, then an
	 * empty {@code Set} is returned. This method will never return a
	 * {@code null} reference.
	 *
	 * @return a {@code Set} of {@code String} IDs for all {@code Dockables}
	 * registered with the framework.
	 * @see #registerDockable(Dockable)
	 * @see Dockable#getPersistentId()
	 */
	public static Set getDockableIds() {
		synchronized (DOCKABLES_BY_ID) {
			return new HashSet(DOCKABLES_BY_ID.keySet());
		}
	}

	/**
	 * Returns the listener object responsible for managing drag-to-dock mouse
	 * events for the specified {@code Dockable}. During registration, the
	 * listener is added to each of the {@code Components} within the
	 * {@code Dockable's} {@code getDragSources()} {@code List}. Thus, for this
	 * method to return a valid {@code DragManager} instance, the
	 * {@code Dockable} must first have been registered via
	 * {@code registerDockable(Dockable dockable)}. If the specified
	 * {@code Dockable} is {@code null} or its {@code getDragSources()} method
	 * returns a {@code null}, or if the {@code Dockable} has not previously
	 * been registered, this method will return a {@code null} reference.
	 *
	 * @param dockable the {@code Dockable} whose drag listener is to be returned.
	 * @return the {@code DragManager} responsible for listening to an managing
	 * drag-related mouse events for the specified {@code Dockable}.
	 * @see DragManager
	 * @see Dockable#getDragSources()
	 * @see #registerDockable(Dockable)
	 */
	public static DragManager getDragListener(Dockable dockable) {
		if (dockable == null || dockable.getDragSources() == null)
			return null;

		for (Iterator it = dockable.getDragSources().iterator(); it.hasNext(); ) {
			Object obj = it.next();
			if (obj instanceof Component) {
				DragManager listener = getDragListener((Component) obj);
				if (listener != null)
					return listener;
			}
		}
		return null;
	}

	private static DragManager getDragListener(Component c) {
		EventListener[] listeners = c.getMouseMotionListeners();
		for (int i = 0; i < listeners.length; i++) {
			if (listeners[i] instanceof DragManager)
				return (DragManager) listeners[i];
		}
		return null;
	}

	/**
	 * Returns the currently installed {@code LayoutManager}. The
	 * {@code LayoutManager} is responsible for managing docking layout state.
	 * This includes tracking the state for all {@code Dockables} as they are
	 * embedded, minimized, floated, or hidden. If a {@code Dockable} is
	 * embedded, the {@code LayoutManager} is responsible for tracking its
	 * position and size relative to other embedded {@code Dockables}. If
	 * floating, the {@code LayoutManager} is responsible for supplying a
	 * {@code FloatManager} to maintain {@code Dockable} groupings within
	 * dialogs as well as dialog size and positioning.
	 * <p>
	 * The {@code LayoutManager} is responsible for providing a persistence
	 * mechanism to save and restore layout states. Depending on the
	 * {@code LayoutManager} implementation, it may or may not support multiple
	 * layout models that may be loaded and switched between at runtime.
	 * <p>
	 * Because the {@code LayoutManager} is a critical piece of the docking
	 * infrastructure, it is not possible to install a {@code null}
	 * {@code LayoutManager}. Therefore, this method will always return a valid
	 * {@code LayoutManager} and never a {@code null} reference.
	 *
	 * @return the currently installed {@code LayoutManager}
	 * @see LayoutManager
	 * @see #setLayoutManager(LayoutManager)
	 * @see #setLayoutManager(String)
	 */
	public static LayoutManager getLayoutManager() {
		return getDockingManager().layoutManager;
	}

	/**
	 * Returns the currently installed {@code MinimizationManager}. The
	 * {@code MinimizationManager} is responsible for minimizing and
	 * unminimizing {@code Dockables}, removing from and restoring to the
	 * embedded docking layout through the currently installed
	 * {@code LayoutManager}.
	 * <p>
	 * The visual representation of a "minimized" {@code Dockable} is somewhat
	 * abstract, although it is commonly expressed in user interfaces with the
	 * disappearance of the {@code Dockable} from the layout and the addition of
	 * a tab or label on one or more edges of the application window. The
	 * {@code MinimizationManager} implementation itself is responsible for
	 * interpreting the visual characteristics and behavior of a minimized
	 * {@code Dockable}, but it must provide a "preview" feature to allow
	 * viewing of minimized {@code Dockables}, on demand without actually
	 * restoring them to the embedded docking layout. {@code Dockables} may or
	 * may not have limited docking functionality while in minimized and/or
	 * preview state, depending upon the {@code MinimizationManager}
	 * implementation.
	 * <p>
	 * Because the {@code MinimizationManager} is a critical piece of the
	 * docking infrastructure, it cannot be set to {@code null}. Therefore,
	 * this method will always return a valid {@code MinimizationManager} and
	 * never a {@code null} reference.
	 *
	 * @return the currently installed {@code MinimizationManager}.
	 * @see MinimizationManager
	 * @see #setMinimizeManager(MinimizationManager)
	 * @see #setMinimizeManager(String)
	 */
	public static MinimizationManager getMinimizeManager() {
		MinimizationManager mgr = getDockingManager().minimizeManager;
		return mgr == null ? MinimizationManager.DEFAULT_STUB : mgr;
	}

	/**
	 * Returns the currently installed {@code FloatManager}. The
	 * {@code FloatManager} is actually provided by the currently installed
	 * {@code LayoutManager}. As such, this method is merely for convenience.
	 * It internally obtains the installed {@code LayoutManager} via
	 * {@code getLayoutManager()} and invokes its {@code getFloatManager()}
	 * method.
	 * <p>
	 * The {@code FloatManager} maintains information relevant to floating
	 * {@code Dockables} including grouping them together within dialogs and
	 * tracking dialog size and position. The {@code FloatManager} is
	 * responsible for generating new dialogs, parenting on the proper
	 * application window(s), and sending {@code Dockables} to the proper
	 * dialogs. It may be used by the {@code LayoutManager} to restore hidden
	 * {@code Dockables} to proper floating state as needed.
	 * <p>
	 * Since the {@code FloatManager} is provided by the currently installed
	 * {@code LayoutManager}, it cannot be set from within the
	 * {@code DockingManager}. To change the installed {@code FloatManager},
	 * one must work directly with the installed {@code LayoutManager}
	 * implementation per its particular custom API.
	 * <p>
	 * Since the {@code FloatManager} is a critical piece of the docking
	 * insfrastructure, this method will never return a {@code null} reference.
	 *
	 * @return the {@code FloatManager} provided by the currently installed
	 * {@code LayoutManager}
	 * @see #getLayoutManager()
	 * @see #setLayoutManager(LayoutManager)
	 * @see LayoutManager#getFloatManager()
	 */
	public static FloatManager getFloatManager() {
		return getLayoutManager().getFloatManager();
	}

	/**
	 * Returns the {@code DockingState} for the {@code Dockable} with the
	 * specified ID. The {@code DockingState} is used by the currently installed
	 * {@code LayoutManager} to track information regarding a {@code Dockable's}
	 * current state in the docking layout. This includes relative size and
	 * positioning to other {@code Dockables}, minimization status, floating
	 * status, and any other information used to track and potentially restore a
	 * the {@code Dockable} to the layout if it is currently hidden.
	 * <p>
	 * The {@code Dockable} whose current {@code DockingState} is resolved will
	 * map to the specified {@code dockableId} via its {@code getPersistentId()}
	 * method. The semantics of this mapping relationship are the same as
	 * {@code DockingManager.getDockable(String id)}. If a valid
	 * {@code Dockable} cannot be found for the specified ID, then this method
	 * returns a {@code null} reference.
	 * <p>
	 * The {@code DockingState} for any given {@code Dockable} is ultimately
	 * managed by the currently installed {@code LayoutManager}. Therefore,
	 * this method resolves the {@code LayoutManager} via
	 * {@code getLayoutManager()} and defers processing to its
	 * {@code getDockingState(String dockableId)} method.
	 * <p>
	 * The underlying {@code LayoutManager} does not provide any guarantees that
	 * the same {@code DockingState} reference always will be returned for a
	 * given {@code Dockable}; only that the returned {@code DockingState} will
	 * accurately reflect the current state maintained by the
	 * {@code LayoutManager} for that {@code Dockable}. For instance, if the
	 * {@code LayoutManager} is capable of maintaining multiple layouts for an
	 * application (as Eclipse does between perspectives), then the
	 * {@code LayoutManager} may or may not maintain multiple
	 * {@code DockingState} instances for a single {@code Dockable}, one within
	 * each layout context. Therefore, it is not a good idea to cache references
	 * to the {@code DockingState} instance returned by this method for future
	 * use as the reference itself may possibly become stale over time depending
	 * on the {@code LayoutManager} implementation.
	 *
	 * @param dockableId the persistent ID of the {@code Dockable} whose current
	 *                   {@code DockingState} is to be returned
	 * @return the current {@code DockingState} maintained by the
	 * {@code LayoutManager} for the specified {@code Dockable}
	 * @see DockingState
	 * @see #getLayoutManager()
	 * @see LayoutManager#getDockingState(String)
	 * @see #getDockable(String)
	 * @see Dockable#getPersistentId()
	 */
	public static DockingState getDockingState(String dockableId) {
		return getLayoutManager().getDockingState(dockableId);
	}

	/**
	 * Returns the {@code DockingState} for the specified {@code Dockable}. The
	 * {@code DockingState} is used by the currently installed
	 * {@code LayoutManager} to track information regarding a {@code Dockable's}
	 * current state in the docking layout. This includes relative size and
	 * positioning to other {@code Dockables}, minimization status, floating
	 * status, and any other information used to track and potentially restore a
	 * the {@code Dockable} to the layout if it is currently hidden.
	 * <p>
	 * If the {@code dockable} parameter is {@code null}, then this method
	 * returns a {@code null} reference.
	 * <p>
	 * The {@code DockingState} for any given {@code Dockable} is ultimately
	 * managed by the currently installed {@code LayoutManager}. Therefore,
	 * this method resolves the {@code LayoutManager} via
	 * {@code getLayoutManager()} and defers processing to its
	 * {@code getDockingState(String dockableId)} method.
	 * <p>
	 * The underlying {@code LayoutManager} does not provide any guarantees that
	 * the same {@code DockingState} reference always will be returned for a
	 * given {@code Dockable}; only that the returned {@code DockingState} will
	 * accurately reflect the current state maintained by the
	 * {@code LayoutManager} for that {@code Dockable}. For instance, if the
	 * {@code LayoutManager} is capable of maintaining multiple layouts for an
	 * application (as Eclipse does between perspectives), then the
	 * {@code LayoutManager} may or may not maintain multiple
	 * {@code DockingState} instances for a single {@code Dockable}, one within
	 * each layout context. Therefore, it is not a good idea to cache references
	 * to the {@code DockingState} instance returned by this method for future
	 * use as the reference itself may possibly become stale over time depending
	 * on the {@code LayoutManager} implementation.
	 *
	 * @param dockable the {@code Dockable} whose current {@code DockingState} is to
	 *                 be returned
	 * @return the current {@code DockingState} maintained by the
	 * {@code LayoutManager} for the specified {@code Dockable}
	 * @see #getLayoutManager()
	 * @see LayoutManager#getDockingState(String)
	 */
	public static DockingState getDockingState(Dockable dockable) {
		return getLayoutManager().getDockingState(dockable);
	}

	/**
	 * Returns the currently installed {@code DockableFactory}. The
	 * {@code DockableFactory} installed by default is {@code null}. Therefore,
	 * this method will return a {@code null} reference until the application
	 * developer explicitly provides a {@code DockableFactory} implementation
	 * via {@code setDockableFactory(DockableFactory factory)}.
	 * <p>
	 * Installing a {@code DockableFactory} allows FlexDock to seamlessly create
	 * and register {@code Dockables} within {@code getDockable(String id)}.
	 * Generally, {@code getDockable(String id)} will lookup the requested
	 * {@code Dockable} within the internal registry. If not found, and there is
	 * no {@code DockableFactory} installed, {@code getDockable(String id)}
	 * returns a {@code null} reference. When a {@code DockableFactory} is
	 * installed, however, failure to lookup a valid {@code Dockable} will cause
	 * {@code getDockable(String id)} to invoke the installed
	 * {@code DockableFactory's} {@code getDockable(String dockableId)} method,
	 * transparently registering and returning the newly created
	 * {@code Dockable} from {@code getDockable(String id)}.
	 *
	 * @return the currently installed {@code DockableFactory}
	 * @see #getDockable(String)
	 * @see DockableFactory#getDockable(String)
	 */
	public static DockableFactory getDockableFactory() {
		return getDockingManager().dockableFactory;
	}

	/**
	 * Enables and disables auto-persistence of the current docking layout model
	 * when the application exits. Auto-persistence is disabled by default.
	 * <p>
	 * The {@code storeLayoutModel()} provides a means of manually sending the
	 * docking layout model to some type of external storage. When the
	 * {@code DockingManager} class loads, a shutdown hook is added to the
	 * {@code Runtime}. If auto-persist is enabled when the JVM exits, the
	 * shutdown hook automatically calls {@code storeLayoutModel()}, catching
	 * and reporting any {@code IOExceptions} that may occur.
	 *
	 * @param enabled {@code true} if automatic persistence is desired;
	 *                {@code false} otherwise.
	 * @see #storeLayoutModel()
	 * @see Runtime#addShutdownHook(java.lang.Thread)
	 */
	public static void setAutoPersist(boolean enabled) {
		getDockingManager().autoPersister.setEnabled(enabled);
	}

	/**
	 * Sets the divider location of the split layout containing the specified
	 * dockable {@code Component}. The {@code Dockable} instance associated
	 * with the specified {@code Component} is resolved via
	 * {@code getDockable(Component comp)} and processing is dispatched to
	 * {@code setSplitProportion(Dockable dockable, float proportion)}.
	 * <p>
	 * The resulting divider location will be a percentage of the split layout
	 * size based upon the {@code proportion} parameter. Valid values for
	 * {@code proportion} range from {@code 0.0F{@code  to {@code 1.0F}. For
	 * example, a {@code proportion} of {@code 0.3F} will move the divider to
	 * 30% of the "size" (<i>width</i> for horizontal split, <i>height</i>
	 * for vertical split) of the split container that contains the specified
	 * {@code Component}. If a {@code proportion} of less than {@code 0.0F} is
	 * supplied, the value }0.0F} is used. If a {@code proportion} greater than
	 * {@code 1.0F} is supplied, the value }1.0F} is used.
	 * <p>
	 * It is important to note that the split divider location is only a
	 * percentage of the container size from left to right or top to bottom. A
	 * {@code proportion} of {@code 0.3F} does not imply that {@code dockable}
	 * itself will be allotted 30% of the available space. The split divider
	 * will be moved to the 30% position of the split container regardless of
	 * the region in which the specified {@code Component} resides (which may
	 * possibly result in {@code dockable} being allotted 70% of the available
	 * space).
	 * <p>
	 * This method should be effective regardless of whether the split layout in
	 * question has been fully realized and is currently visible on the screen.
	 * This should alleviate common problems associated with setting percentages
	 * of unrealized {@code Component} dimensions, which are initially
	 * {@code 0x0} before the {@code Component} has been rendered to the screen.
	 * <p>
	 * If the specified {@code Component} is {@code null}, then no
	 * {@code Exception} is thrown and no action is taken. Identical behavior
	 * occurs if a valid {@code Dockable} cannot be resolved for the specified
	 * {@code Component}, or the {@code Dockable} does not reside within a
	 * split layout.
	 * <p>
	 * If the {@code Dockable} resides within a tabbed layout, a check is done
	 * to see if the tabbed layout resides within a parent split layout. If so,
	 * the resolved split layout is resized. Otherwise no action is taken.
	 *
	 * @param dockable   the {@code Component} whose containing split layout is to be
	 *                   resized.
	 * @param proportion the percentage of containing split layout size to which the
	 *                   split divider should be set.
	 * @see #setSplitProportion(Dockable, float)
	 * @see #getDockable(Component)
	 */
	public static void setSplitProportion(Component dockable, float proportion) {
		setSplitProportion(getDockable(dockable), proportion);
	}

	/**
	 * Sets the divider location of the split layout containing the specified
	 * dockable {@code Component}.
	 * <p>
	 * The resulting divider location will be a percentage of the split layout
	 * size based upon the {@code proportion} parameter. Valid values for
	 * {@code proportion} range from {@code 0.0F{@code  to {@code 1.0F}. For
	 * example, a {@code proportion} of {@code 0.3F} will move the divider to
	 * 30% of the "size" (<i>width</i> for horizontal split, <i>height</i>
	 * for vertical split) of the split container that contains the specified
	 * {@code Dockable}. If a {@code proportion} of less than {@code 0.0F} is
	 * supplied, the value }0.0F} is used. If a {@code proportion} greater than
	 * {@code 1.0F} is supplied, the value }1.0F} is used.
	 * <p>
	 * It is important to note that the split divider location is only a
	 * percentage of the container size from left to right or top to bottom. A
	 * {@code proportion} of {@code 0.3F} does not imply that {@code dockable}
	 * itself will be allotted 30% of the available space. The split divider
	 * will be moved to the 30% position of the split container regardless of
	 * the region in which the specified {@code Dockable} resides (which may
	 * possibly result in {@code dockable} being allotted 70% of the available
	 * space).
	 * <p>
	 * This method should be effective regardless of whether the split layout in
	 * question has been fully realized and is currently visible on the screen.
	 * This should alleviate common problems associated with setting percentages
	 * of unrealized {@code Component} dimensions, which are initially
	 * {@code 0x0} before the {@code Component} has been rendered to the screen.
	 * <p>
	 * If the specified {@code Dockable} is {@code null}, then no
	 * {@code Exception} is thrown and no action is taken. Identical behavior
	 * occurs if the {@code Dockable} does not reside within a split layout.
	 * <p>
	 * If the {@code Dockable} resides within a tabbed layout, a check is done
	 * to see if the tabbed layout resides within a parent split layout. If so,
	 * the resolved split layout is resized. Otherwise no action is taken.
	 *
	 * @param dockable   the {@code Dockable} whose containing split layout is to be
	 *                   resized.
	 * @param proportion the percentage of containing split layout size to which the
	 *                   split divider should be set.
	 * @see #getDockable(Component)
	 */
	public static void setSplitProportion(Dockable dockable, float proportion) {
		DockingUtility.setSplitProportion(dockable, proportion);
	}

	/**
	 * Sets the divider location of the split layout embedded within the
	 * specified {@code DockingPort}. This method differs from both
	 * {@code setSplitProportion(Component dockable, float proportion)} and
	 * {@code setSplitProportion(Dockable dockable, float proportion)} in that
	 * this method resolves the split layout embedded <b>within</b> the
	 * specified {@code DockingPort}, whereas the other methods modify the
	 * split layout <b>containing</b> their respective {@code Dockable}
	 * parameters.
	 * <p>
	 * The resulting divider location will be a percentage of the split layout
	 * size based upon the {@code proportion} parameter. Valid values for
	 * {@code proportion} range from {@code 0.0F{@code  to {@code 1.0F}. For
	 * example, a {@code proportion} of {@code 0.3F} will move the divider to
	 * 30% of the "size" (<i>width</i> for horizontal split, <i>height</i>
	 * for vertical split) of the split container embedded within the specified
	 * {@code DockingPort}. If a {@code proportion} of less than {@code 0.0F}
	 * is supplied, the value }0.0F} is used. If a {@code proportion} greater
	 * than {@code 1.0F} is supplied, the value }1.0F} is used.
	 * <p>
	 * This method should be effective regardless of whether the split layout in
	 * question has been fully realized and is currently visible on the screen.
	 * This should alleviate common problems associated with setting percentages
	 * of unrealized {@code Component} dimensions, which are initially
	 * {@code 0x0} before the {@code Component} has been rendered to the screen.
	 * <p>
	 * If the specified {@code DockingPort} is {@code null}, then no
	 * {@code Exception} is thrown and no action is taken. Identical behavior
	 * occurs if the {@code DockingPort} does not contain split layout.
	 *
	 * @param port       the {@code DockingPort} containing the split layout is to be
	 *                   resized.
	 * @param proportion the percentage of split layout size to which the split divider
	 *                   should be set.
	 */
	public static void setSplitProportion(DockingPort port, float proportion) {
		DockingUtility.setSplitProportion(port, proportion);
	}

	/**
	 * Sets the currently installed {@code DockableFactory}. {@code null}
	 * values for the {@code factory} parameter are acceptable.
	 * <p>
	 * Installing a {@code DockableFactory} allows FlexDock to seamlessly create
	 * and register {@code Dockables} within {@code getDockable(String id)}.
	 * Generally, {@code getDockable(String id)} will lookup the requested
	 * {@code Dockable} within the internal registry. If not found, and there is
	 * no {@code DockableFactory} installed, {@code getDockable(String id)}
	 * returns a {@code null} reference. When a {@code DockableFactory} is
	 * installed, however, failure to lookup a valid {@code Dockable} will cause
	 * {@code getDockable(String id)} to invoke the installed
	 * {@code DockableFactory's} {@code getDockable(String dockableId)} method,
	 * transparently registering and returning the newly created
	 * {@code Dockable} from {@code getDockable(String id)}.
	 *
	 * @param factory the {@code DockableFactory} to install
	 * @see #getDockableFactory()
	 * @see #getDockable(String)
	 * @see DockableFactory#getDockable(String)
	 */
	public static void setDockableFactory(DockableFactory factory) {
		getDockingManager().dockableFactory = factory;
	}

	/**
	 * Sets the minimized state for the specified {@code Dockable}. This method
	 * defers processing to
	 * {@code setMinimized(Dockable dockable, boolean minimized, Component window)},
	 * passing the current {@code Window} ancestor of the specified
	 * {@code Dockable} as the {@code window} parameter. Minimization
	 * processessing is ultimately deferred to the currently installed
	 * {@code MinimizationManager} with a constraint of
	 * {@code MinimizationManager.UNSPECIFIED_LAYOUT_CONSTRAINT}.
	 * <p>
	 * The current {@code MinimizationManager} is responsible for updating the
	 * underlying {@code DockingState} model for the specified {@code Dockable}
	 * as well as rendering its own interpretation of the corresponding visual
	 * state on the screen. If the supplied {@code minimized} parameter matches
	 * the current {@code DockingState}, the {@code MinimizationManager} is
	 * responsible for providing the appropriate visual indications, or lack
	 * thereof. If the specified {@code Dockable} is {@code null}, no
	 * {@code Exception} is thrown and no action is taken.
	 *
	 * @param dockable  the {@code Dockable} whose minimzed state is to be modified
	 * @param minimized {@code true} if the specified {@code Dockable} should be
	 *                  minimized, {@code false} otherwise.
	 * @see #setMinimized(Dockable, boolean, Component)
	 * @see #getMinimizeManager()
	 * @see MinimizationManager#setMinimized(Dockable, boolean, Component, int)
	 * @see DockingState#getMinimizedConstraint()
	 */
	public static void setMinimized(Dockable dockable, boolean minimized) {
		Component cmp = dockable == null ? null : dockable.getComponent();
		Window window = cmp == null ? null : SwingUtilities
				.getWindowAncestor(cmp);
		setMinimized(dockable, minimized, window);
	}

	/**
	 * Sets the minimized state for the specified {@code Dockable}. This method
	 * defers processing to
	 * {@code setMinimized(Dockable dockable, boolean minimizing, Component window, int constraint)},
	 * passing {@code MinimizationManager.UNSPECIFIED_LAYOUT_CONSTRAINT} for the
	 * {@code constraint} parameter. Minimization processessing is ultimately
	 * deferred to the currently installed {@code MinimizationManager}.
	 * <p>
	 * The {@code window} parameter is passed to the {@code MinimizationManager}
	 * to indicate that minimization should be handled with respect to the
	 * specified root window, or the root window containing the specified
	 * {@code Component}. {@code null} values are acceptable for this
	 * parameter.
	 * <p>
	 * The current {@code MinimizationManager} is responsible for updating the
	 * underlying {@code DockingState} model for the specified {@code Dockable}
	 * as well as rendering its own interpretation of the corresponding visual
	 * state on the screen. If the supplied {@code minimized} parameter matches
	 * the current {@code DockingState}, the {@code MinimizationManager} is
	 * responsible for providing the appropriate visual indications, or lack
	 * thereof. If the specified {@code Dockable} is {@code null}, no
	 * {@code Exception} is thrown and no action is taken.
	 *
	 * @param dockable  the {@code Dockable} whose minimzed state is to be modified
	 * @param minimized {@code true} if the specified {@code Dockable} should be
	 *                  minimized, {@code false} otherwise.
	 * @param window    the {@code Component} whose root window will be used by the
	 *                  underlying {@code MinimizationManager} for rendering the
	 *                  {@code Dockable} in its new minimized state.
	 * @see #setMinimized(Dockable, boolean, Component, int)
	 * @see #getMinimizeManager()
	 * @see MinimizationManager#setMinimized(Dockable, boolean, Component, int)
	 * @see DockingState#getMinimizedConstraint()
	 */
	public static void setMinimized(Dockable dockable, boolean minimized,
	                                Component window) {
		setMinimized(dockable, minimized, window,
				MinimizationManager.UNSPECIFIED_LAYOUT_CONSTRAINT);
	}

	/**
	 * Sets the minimized state for the specified {@code Dockable}. This method
	 * defers processing to
	 * {@code setMinimized(Dockable dockable, boolean minimizing, Component window, int constraint)},
	 * passing {@code null} for the {@code window} parameter. Minimization
	 * processessing is ultimately deferred to the currently installed
	 * {@code MinimizationManager}.
	 * <p>
	 * Valid values for the {@code constraint} parameter may be found on the
	 * {@code MinimizationManager} interface and include
	 * UNSPECIFIED_LAYOUT_CONSTRAINT, TOP, LEFT, BOTTOM, RIGHT, and CENTER.
	 * However, constraint values must ultimately be interpreted by the current
	 * {@code MinimizationManager} implementation and, thus any integer value
	 * may theoretically be valid for {@code constraint}.
	 * <p>
	 * The current {@code MinimizationManager} is responsible for updating the
	 * underlying {@code DockingState} model for the specified {@code Dockable}
	 * as well as rendering its own interpretation of the corresponding visual
	 * state on the screen. If the supplied {@code minimized} parameter matches
	 * the current {@code DockingState}, the {@code MinimizationManager} is
	 * responsible for providing the appropriate visual indications, or lack
	 * thereof. If the specified {@code Dockable} is {@code null}, no
	 * {@code Exception} is thrown and no action is taken.
	 *
	 * @param dockable   the {@code Dockable} whose minimzed state is to be modified
	 * @param minimizing {@code true} if the specified {@code Dockable} should be
	 *                   minimized, {@code false} otherwise.
	 * @param constraint a value to indicate to the {@code MinimizationManager} desired
	 *                   rendering of the minimized {@code Dockable}
	 * @see #setMinimized(Dockable, boolean, Component, int)
	 * @see #getMinimizeManager()
	 * @see MinimizationManager#setMinimized(Dockable, boolean, Component, int)
	 * @see DockingState#getMinimizedConstraint()
	 */
	public static void setMinimized(Dockable dockable, boolean minimizing,
	                                int constraint) {
		setMinimized(dockable, minimizing, null, constraint);
	}

	/**
	 * Sets the minimized state for the specified {@code Dockable}. This method
	 * defers processing to the currently installed {@code MinimizationManager}.
	 * <p>
	 * The {@code window} parameter is passed to the {@code MinimizationManager}
	 * to indicate that minimization should be handled with respect to the
	 * specified root window, or the root window containing the specified
	 * {@code Component}. If a {@code null} values is supplied for this
	 * parameter, the currently active window is used. If no currently active
	 * window can be determined, then this method exits with no action taken.
	 * <p>
	 * The current {@code MinimizationManager} is responsible for updating the
	 * underlying {@code DockingState} model for the specified {@code Dockable}
	 * as well as rendering its own interpretation of the corresponding visual
	 * state on the screen. If the supplied {@code minimized} parameter matches
	 * the current {@code DockingState}, the {@code MinimizationManager} is
	 * responsible for providing the appropriate visual indications, or lack
	 * thereof. If the specified {@code Dockable} is {@code null}, no
	 * {@code Exception} is thrown and no action is taken.
	 * <p>
	 * Valid values for the {@code constraint} parameter may be found on the
	 * {@code MinimizationManager} interface and include
	 * UNSPECIFIED_LAYOUT_CONSTRAINT, TOP, LEFT, BOTTOM, RIGHT, and CENTER.
	 * However, constraint values must ultimately be interpreted by the current
	 * {@code MinimizationManager} implementation and, thus any integer value
	 * may theoretically be valid for {@code constraint}.
	 *
	 * @param dockable   the {@code Dockable} whose minimzed state is to be modified
	 * @param minimizing {@code true} if the specified {@code Dockable} should be
	 *                   minimized, {@code false} otherwise.
	 * @param window     the {@code Component} whose root window will be used by the
	 *                   underlying {@code MinimizationManager} for rendering the
	 *                   {@code Dockable} in its new minimized state.
	 * @param constraint a value to indicate to the {@code MinimizationManager} desired
	 *                   rendering of the minimized {@code Dockable}
	 * @see #getMinimizeManager()
	 * @see MinimizationManager#setMinimized(Dockable, boolean, Component, int)
	 * @see DockingState#getMinimizedConstraint()
	 */
	public static void setMinimized(Dockable dockable, boolean minimizing,
	                                Component window, int constraint) {
		if (dockable == null)
			return;

		if (window == null) {
			window = applicationWindow;
		}
		if (window == null) {
			window = SwingUtility.getActiveWindow();
		}
		if (window == null)
			return;

		getMinimizeManager().setMinimized(dockable, minimizing, window,
				constraint);
	}

	/**
	 * Sets the "main" {@code DockingPort} within the application window
	 * containing the specified {@code Component}. Just as desktop applications
	 * will tend to have a "main" application window, perhaps surrounded with
	 * satellite windows or dialogs, the "main" {@code DockingPort} within a
	 * given window will be considered by the application developer to contain
	 * the primary docking layout used by the enclosing window.
	 * <p>
	 * The {@code Component} parameter may or may not be a root window
	 * container. If not, the ancestor window of {@code comp} is determined and
	 * a set of docking ports encapsulated by a {@code RootDockingPortInfo}
	 * instance is returned by a call to
	 * {@code getRootDockingPortInfo(Component comp)}. The resolved
	 * {@code RootDockingPortInfo} instance's main {@code DockingPort} is set
	 * via its method {@code setMainPort(String portId)}.
	 * <p>
	 * By default, the "main" {@code DockingPort} assigned to any
	 * {@code RootDockingPortInfo} instance associated with a window will happen
	 * to be the first root {@code DockingPort} detected for that window. This
	 * method is used to alter that setting.
	 * <p>
	 * If {@code comp} is {@code null} or the root window cannot be resolved,
	 * then this method returns with no action taken.
	 *
	 * @param window the {@code Component} whose root window will be checked for a
	 *               main {@code DockingPort}
	 * @param portId the persistent ID of the {@code DockingPort} to use as the
	 *               main {@code DockingPort} for the specified window.
	 * @see #getRootDockingPortInfo(Component)
	 * @see #getRootDockingPort(Component)
	 * @see DockingPortTracker#getRootDockingPortInfo(Component)
	 * @see RootDockingPortInfo#getMainPort()
	 * @see RootDockingPortInfo#setMainPort(String)
	 */
	public static void setMainDockingPort(Component window, String portId) {
		RootDockingPortInfo info = getRootDockingPortInfo(window);
		if (info != null)
			info.setMainPort(portId);
	}

	/**
	 * Sets the currently installed {@code MinimizationManager}. The
	 * {@code MinimizationManager} is responsible for minimizing and
	 * unminimizing {@code Dockables}, removing from and restoring to the
	 * embedded docking layout through the currently installed
	 * {@code LayoutManager}.
	 * <p>
	 * The visual representation of a "minimized" {@code Dockable} is somewhat
	 * abstract, although it is commonly expressed in user interfaces with the
	 * disappearance of the {@code Dockable} from the layout and the addition of
	 * a tab or label on one or more edges of the application window. The
	 * {@code MinimizationManager} implementation itself is responsible for
	 * interpreting the visual characteristics and behavior of a minimized
	 * {@code Dockable}, but it must provide a "preview" feature to allow
	 * viewing of minimized {@code Dockables}, on demand without actually
	 * restoring them to the embedded docking layout. {@code Dockables} may or
	 * may not have limited docking functionality while in minimized and/or
	 * preview state, depending upon the {@code MinimizationManager}
	 * implementation.
	 * <p>
	 * Because the {@code MinimizationManager} is a critical piece of the
	 * docking infrastructure, it cannot be set to {@code null}. If a
	 * {@code null} value is passed into this method, the default
	 * {@code MinimizationManager} provided by the framework is used instead.
	 *
	 * @param mgr the {@code MinimizationManager} to be installed
	 * @see MinimizationManager
	 * @see #getMinimizeManager()
	 * @see #setMinimizeManager(String)
	 */
	public static void setMinimizeManager(MinimizationManager mgr) {
		DockingManager dockingManager = getDockingManager();
		if (mgr == null)
			// do not allow null minimization managers
			setMinimizeManager(dockingManager.defaultMinimizeManagerClass);
		else
			dockingManager.minimizeManager = mgr;
	}

	/**
	 * Sets the currently installed {@code MinimizationManager} using the
	 * specfied class name. An attempt is make to instantiate a
	 * {@code MinimizationManager} based upon the supplied class name
	 * {@code String}. If the class cannot be instaniated, a stacktrace is
	 * reported to the System.err and the default {@code MinimizationManager}
	 * supplied by the framework is used. If the {@code String} parameter is
	 * {@code null}, no error occurs and the default
	 * {@code MinimizationManager} is used. If the instantiated class is not a
	 * valid instance of {@code MinimizationManager}, then a
	 * {@code ClassCastException} is thrown.
	 * <p>
	 * The {@code MinimizationManager} is responsible for minimizing and
	 * unminimizing {@code Dockables}, removing from and restoring to the
	 * embedded docking layout through the currently installed
	 * {@code LayoutManager}.
	 * <p>
	 * The visual representation of a "minimized" {@code Dockable} is somewhat
	 * abstract, although it is commonly expressed in user interfaces with the
	 * disappearance of the {@code Dockable} from the layout and the addition of
	 * a tab or label on one or more edges of the application window. The
	 * {@code MinimizationManager} implementation itself is responsible for
	 * interpreting the visual characteristics and behavior of a minimized
	 * {@code Dockable}, but it must provide a "preview" feature to allow
	 * viewing of minimized {@code Dockables}, on demand without actually
	 * restoring them to the embedded docking layout. {@code Dockables} may or
	 * may not have limited docking functionality while in minimized and/or
	 * preview state, depending upon the {@code MinimizationManager}
	 * implementation.
	 * <p>
	 * Because the {@code MinimizationManager} is a critical piece of the
	 * docking infrastructure, it cannot be set to {@code null}. If a
	 * {@code null} value is passed into this method, the default
	 * {@code MinimizationManager} provided by the framework is used instead.
	 *
	 * @param mgrClass the class name of the {@code MinimizationManager} to be
	 *                 installed
	 * @see MinimizationManager
	 * @see #getMinimizeManager()
	 * @see #setMinimizeManager(String)
	 */
	public static void setMinimizeManager(String mgrClass) {
		Object instance = Utilities.getInstance(mgrClass);
		setMinimizeManager((MinimizationManager) instance);
	}

	/**
	 * Sets whether global floating support should be enabled. Defers processing
	 * to
	 * {@code FloatPolicyManager.setGlobalFloatingEnabled(boolean globalFloatingEnabled)}.
	 *
	 * @param enabled {@code true} if global floating support should be enabled,
	 *                {@code false} otherwise.
	 * @see FloatPolicyManager#setGlobalFloatingEnabled(boolean)
	 * @see FloatPolicyManager#isGlobalFloatingEnabled()
	 */
	public static void setFloatingEnabled(boolean enabled) {
		FloatPolicyManager.setGlobalFloatingEnabled(enabled);
	}

	public static void setDefaultPersistenceKey(String key) {
		getLayoutManager().setDefaultPersistenceKey(key);
	}

	public static String getDefaultPersistenceKey() {
		return getLayoutManager().getDefaultPersistenceKey();
	}

	/**
	 * Sets whether tabbed layouts are supported by default for
	 * {@code DockingPorts} with a single {@code Dockable} in the CENTER region.
	 * This is a global default setting and applies to any {@code DockingPort}
	 * that does not have a specific contradictory local setting.
	 * <p>
	 * This method defers processing to
	 * {@code org.flexdock.docking.props.PropertyManager.getDockingPortRoot()}.
	 * As such, there are multiple "scopes" at which this property may be
	 * overridden.
	 *
	 * @param allowed {@code true} if the default setting for {@code DockingPorts}
	 *                should allow a tabbed layout for a single {@code Dockable} in
	 *                the CENTER region; {@code false} otherwise.
	 * @see PropertyManager#getDockingPortRoot()
	 * @see org.flexdock.docking.props.DockingPortPropertySet#setSingleTabsAllowed(boolean)
	 */
	public static void setSingleTabsAllowed(boolean allowed) {
		PropertyManager.getDockingPortRoot().setSingleTabsAllowed(allowed);
	}

	/**
	 * Sets the currently installed {@code LayoutManager}. The
	 * {@code LayoutManager} is responsible for managing docking layout state.
	 * This includes tracking the state for all {@code Dockables} as they are
	 * embedded, minimized, floated, or hidden. If a {@code Dockable} is
	 * embedded, the {@code LayoutManager} is responsible for tracking its
	 * position and size relative to other embedded {@code Dockables}. If
	 * floating, the {@code LayoutManager} is responsible for supplying a
	 * {@code FloatManager} to maintain {@code Dockable} groupings within
	 * dialogs as well as dialog size and positioning.
	 * <p>
	 * The {@code LayoutManager} is responsible for providing a persistence
	 * mechanism to save and restore layout states. Depending on the
	 * {@code LayoutManager} implementation, it may or may not support multiple
	 * layout models that may be loaded and switched between at runtime.
	 * <p>
	 * Because the {@code LayoutManager} is a critical piece of the docking
	 * infrastructure, it is not possible to install a {@code null}
	 * {@code LayoutManager}. FlexDock provides a default {@code LayoutManager}
	 * implementation. If this method is passed a {@code null} argument, the
	 * default {@code LayoutManager} is used instead.
	 *
	 * @param mgr the {@code LayoutManager} to install.
	 * @see LayoutManager
	 * @see #setLayoutManager(String)
	 * @see #getLayoutManager()
	 */
	public static void setLayoutManager(LayoutManager mgr) {
		DockingManager dockingManager = getDockingManager();
		if (mgr == null)
			// do not allow a null layout manager.
			setLayoutManager(dockingManager.defaultLayoutManagerClass);
		else
			getDockingManager().layoutManager = mgr;
	}

	/**
	 * Sets the currently installed {@code LayoutManager} using the specified
	 * class name. An attempt is make to instantiate a {@code LayoutManager}
	 * based upon the supplied class name {@code String}. If the class cannot
	 * be instaniated, a stacktrace is reported to the System.err and the
	 * default {@code LayoutManager} supplied by the framework is used. If the
	 * {@code String} parameter is {@code null}, no error occurs and the
	 * default {@code LayoutManager} is used. If the instantiated class is not a
	 * valid instance of {@code LayoutManager}, then a
	 * {@code ClassCastException} is thrown.
	 * <p>
	 * The {@code LayoutManager} is responsible for managing docking layout
	 * state. This includes tracking the state for all {@code Dockables} as they
	 * are embedded, minimized, floated, or hidden. If a {@code Dockable} is
	 * embedded, the {@code LayoutManager} is responsible for tracking its
	 * position and size relative to other embedded {@code Dockables}. If
	 * floating, the {@code LayoutManager} is responsible for supplying a
	 * {@code FloatManager} to maintain {@code Dockable} groupings within
	 * dialogs as well as dialog size and positioning.
	 * <p>
	 * The {@code LayoutManager} is responsible for providing a persistence
	 * mechanism to save and restore layout states. Depending on the
	 * {@code LayoutManager} implementation, it may or may not support multiple
	 * layout models that may be loaded and switched between at runtime.
	 * <p>
	 * Because the {@code LayoutManager} is a critical piece of the docking
	 * infrastructure, it is not possible to install a {@code null}
	 * {@code LayoutManager}. FlexDock provides a default {@code LayoutManager}
	 * implementation. If this method is passed a {@code null} argument, the
	 * default {@code LayoutManager} is used instead.
	 *
	 * @param mgrClass the class name of the {@code LayoutManager} to install.
	 * @see LayoutManager
	 * @see #setLayoutManager(LayoutManager)
	 * @see #getLayoutManager()
	 */
	public static void setLayoutManager(String mgrClass) {
		Object instance = Utilities.getInstance(mgrClass);
		setLayoutManager((LayoutManager) instance);
	}

	/**
	 * Sets the {@code DockingStrategy} associated with specified {@code Class}.
	 * This method returns with no action taken if the specified {@code Class}
	 * paramter is {@code null}. If the {@code strategy} parameter is
	 * {@code null} then any existing {@code DockingStrategy} association with
	 * the specified }Class} is removed. Otherwise, a new
	 * {@code DockingStrategy} association is added for the specified
	 * {@code Class}.
	 * <p>
	 * {@code DockingStrategy} association follows a strict inheritance chain
	 * using {@code org.flexdock.util.ClassMapping}. This means that the
	 * association created by this method applies for the specified
	 * {@code Class} and all direct subclasses, but associations for interfaces
	 * are ignored. Associations also do not apply for subclasses that have
	 * their own specific {@code DockingStrategy} mapping.
	 * <p>
	 *
	 * @param classKey the {@code Class} whose {@code DockingStrategy} association we
	 *                 wish to set
	 * @param strategy the {@code DockingStrategy} to be associated with the
	 *                 specified {@code Class}.
	 * @see #getDockingStrategy(Class)
	 * @see #getDockingStrategy(Object)
	 * @see ClassMapping#addClassMapping(Class, Class, Object)
	 * @see ClassMapping#removeClassMapping(Class)
	 */
	public static void setDockingStrategy(Class classKey,
	                                      DockingStrategy strategy) {
		if (classKey == null)
			return;

		if (strategy == null)
			DOCKING_STRATEGIES.removeClassMapping(classKey);
		else
			DOCKING_STRATEGIES.addClassMapping(classKey, strategy.getClass(),
					strategy);
	}

	/**
	 * Undocks the specified {@code Dockable} from its parent
	 * {@code DockingPort}. If the {@code Dockable} is {@code null}, or it
	 * does not currently reside within a {@code DockingPort}, then this method
	 * returns {@code false} with no action taken. Otherwise, this method
	 * returns {@code true} if the undocking operation was successful and
	 * {@code false} if the undocking operation could not be completed.
	 * <p>
	 * This method determines the {@code DockingStrategy} to be used for
	 * {@code DockingPort} containing the specified {@code Dockable} and defers
	 * processing to the {@code undock(Dockable dockable)} method on the
	 * {@code DockingStrategy}. This method's return value will be based upon
	 * the {@code DockingStrategy} implementation returned by a call to
	 * {@code getDockingStrategy(Object obj)}. The {@code DockingStrategy} used
	 * may be controlled via
	 * {@code setDockingStrategy(Class c, DockingStrategy strategy)}, supplying
	 * a {@code DockingPort} implementation class and a customized
	 * {@code DockingStrategy}.
	 *
	 * @param dockable the {@code Dockable} to be undocked.
	 * @return {@code true} if the undocking operation was successful,
	 * {@code false} otherwise.
	 * @see DockingStrategy#undock(Dockable)
	 * @see #getDockingStrategy(Object)
	 * @see #setDockingStrategy(Class, DockingStrategy)
	 */
	public static boolean undock(Dockable dockable) {
		if (dockable == null)
			return false;

		DockingStrategy strategy = findDockingStrategy(dockable);
		if (strategy != null) {
			return strategy.undock(dockable);
		}

		return false; // TODO think of changing it to runtime exception I
		// don't see a situation when there would be no default docker.
	}

	public static boolean undock(final Component dockable) {
		return undock(resolveDockable(dockable));
	}

	/**
	 * Ensures that a valid {@code DragManager} has been installed as a listener
	 * for all of the specified {@code Dockable's} drag source
	 * {@code Components}. This method invokes the {@code getDragSources()}
	 * method on the specified {@code Dockable} and iterates over each
	 * {@code Component} in the returned {@code List}. If any {@code Component}
	 * does not have a valid {@code DragManager} listener installed, an
	 * appropriate listener is added to enable drag-to-dock functionality.
	 * <p>
	 * This method is useful to application developers who manually attempt to
	 * add new {@code Components} to a {@code Dockable's} drag source
	 * {@code List}. However, it is not necessary to call this method unless
	 * the drag source list has been updated <b>after</b> calling
	 * {@code registerDockable(Dockable dockable)}, since
	 * {@code registerDockable(Dockable dockable)} will automatically initialize
	 * each drag source for the specified {@code Dockable}.
	 * <p>
	 * If the specified {@code Dockable} is {@code null}, then no
	 * {@code Exception} is thrown and no action is taken.
	 *
	 * @param dockable the {@code Dockable} whose drag sources are to be checked for
	 *                 {@code DragManagers} and updated accordingly.
	 * @see #registerDockable(Dockable)
	 * @see Dockable#getDragSources()
	 * @see DragManager
	 */
	public static void updateDragListeners(Dockable dockable) {
		if (dockable == null)
			return;

		DragManager dragListener = getDragListener(dockable);
		if (dragListener == null) {
			dragListener = new DragManager(dockable);
		}

		for (Iterator it = dockable.getDragSources().iterator(); it.hasNext(); ) {
			Object obj = it.next();
			if (obj instanceof Component) {
				updateDragListeners((Component) obj, dragListener);
			}
		}
	}

	private static void removeDragListeners(Dockable dockable) {
		if (dockable == null)
			return;

		for (Iterator it = dockable.getDragSources().iterator(); it.hasNext(); ) {
			Object obj = it.next();
			if (obj instanceof Component) {
				removeDragListeners((Component) obj);
			}
		}
	}

	public static float getDefaultSiblingSize() {
		return getDockingManager().defaultSiblingSize;
	}

	public static void setDefaultSiblingSize(float size) {
		size = Math.max(size, 0);
		size = Math.min(size, 1);
		getDockingManager().defaultSiblingSize = size;
	}

	public static void setRubberBand(RubberBand rubberBand) {
		EffectsManager.setRubberBand(rubberBand);
	}

	public static void setDragPreview(DragPreview dragPreview) {
		EffectsManager.setPreview(dragPreview);
	}

	/**
	 * Maximizes the {@code Dockable} associated with the specified component or
	 * restores the {@code Dockable} if it is currently maximized. This method
	 * forwards the request to {@link #toggleMaximized(Dockable)} after
	 * obtaining the {@code Dockable} associated to the component via
	 * {@link #getDockable(Component)}.
	 *
	 * @param comp
	 * @see #toggleMaximized(Dockable)
	 */
	public static void toggleMaximized(Component comp) {
		Dockable dockable = getDockable(comp);
		if (dockable == null) {
			return;
		}
		toggleMaximized(dockable);
	}

	/**
	 * Maximizes the specified {@code Dockable} or restores the specified
	 * {@code Dockable} if it is already maximized.
	 * <p>
	 * The scope of maximization is the <i>root</i> {@code DockingPort}. The
	 * specified {@code Dockable}'s current {@code DockingPort} is asked to
	 * temporarily lend the {@code Dockable} for maximization and the root
	 * {@code DockingPort} is asked to temorarily host the {@code Dockable} and
	 * display it such that it occupies all (or the majority) of its screen
	 * resources. If the {@code Dockable} is already maximized, the root
	 * {@code DockingPort} is asked to return to its original state and the
	 * {@code Dockable} is returned to its original {@code DockingPort}.
	 *
	 * @param dockable
	 */
	public static void toggleMaximized(Dockable dockable) {
		DockingPort rootPort = getRootDockingPort(dockable.getComponent());
		MaximizedState state = getMaximizedState(rootPort);
		if (state != null) {
			if (state.getDockable() != dockable) {
				throw new IllegalStateException(
						"Can't maximize while different dockable is maximized");
				// maybe silently switch maximized dockables instead?
			}
			restoreFromMaximized(dockable, rootPort, state);
		} else {
			maximize(dockable, rootPort);
		}
	}

	public static boolean isMaximized(Dockable dockable) {
		DockingPort rootPort = getRootDockingPort(dockable.getComponent());
		MaximizedState state = getMaximizedState(rootPort);

		return state != null && state.getDockable().equals(dockable);
	}

	private static void maximize(Dockable dockable, DockingPort rootPort) {
		DockingPort originalPort = dockable.getDockingPort();
		MaximizedState state = new MaximizedState(dockable, originalPort);

		originalPort.releaseForMaximization(dockable);
		rootPort.installMaximizedDockable(dockable);

		maximizedStatesByRootPort.put(rootPort, state);
	}

	private static void restoreFromMaximized(Dockable dockable,
	                                         DockingPort rootPort, MaximizedState state) {

		// restore original state in reverse order than maximizing it
		// (otherwise this will not work if original port and root port are
		// identical)
		rootPort.uninstallMaximizedDockable();
		state.getOriginalPort().returnFromMaximization();

		maximizedStatesByRootPort.remove(rootPort);
	}

	private static MaximizedState getMaximizedState(DockingPort rootPort) {
		return (MaximizedState) maximizedStatesByRootPort.get(rootPort);
	}
}
